Saya memiliki file CSV yang terlihat seperti ini
AS2345, ASDF1232, Mr. Plain Example, 110 Binary ave., Atlantis, RI, 12345, (999) 123-5555,1.56 AS2345, ASDF1232, Nyonya Plain Contoh, 1121110 Ternary st. 110 Binary ave .., Atlantis, RI, 12345, (999) 123-5555,1.56 AS2345, ASDF1232, Mr. Plain Example, 110 Binary ave., Liberty City, RI, 12345, (999) 123-5555,1.56 AS2345, ASDF1232, Mr. Plain Example, 110 Ternary ave., Some City, RI, 12345, (999) 123-5555,1.56
Saya perlu mengurutkan berdasarkan panjang baris termasuk spasi. Perintah berikut tidak menyertakan spasi, apakah ada cara untuk memodifikasinya agar berfungsi untuk saya?
cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'
Jawaban:
Menjawab
cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-
Atau, untuk melakukan sub-pengurutan asli (mungkin tidak disengaja) dari garis yang sama panjangnya:
cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2-
Dalam kedua kasus tersebut, kami telah menyelesaikan masalah yang Anda nyatakan dengan beralih dari awk untuk potongan terakhir Anda.
Garis dengan panjang yang cocok - apa yang harus dilakukan dalam kasus dasi:
Pertanyaan tersebut tidak menentukan apakah pengurutan lebih lanjut diperlukan untuk garis yang cocok atau tidak. Saya berasumsi bahwa ini tidak diinginkan dan menyarankan penggunaan
-s
(--stable
) untuk mencegah baris seperti itu diurutkan satu sama lain, dan menyimpannya dalam urutan relatif di mana mereka terjadi dalam input.(Mereka yang ingin lebih mengontrol penyortiran ikatan ini mungkin melihat
--key
opsi sortir .)Mengapa solusi yang dicoba dari pertanyaan tersebut gagal (pembangunan kembali baris awk):
Menarik untuk diperhatikan perbedaan antara:
echo "hello awk world" | awk '{print}' echo "hello awk world" | awk '{$1="hello"; print}'
Mereka menghasilkan masing-masing
Bagian yang relevan dari manual (gawk) hanya menyebutkan sebagai tambahan bahwa awk akan membangun kembali seluruh $ 0 (berdasarkan pemisah, dll) saat Anda mengubah satu bidang. Saya kira itu bukan perilaku gila. Ini memiliki ini:
"Terakhir, ada kalanya nyaman untuk memaksa awk untuk membangun kembali seluruh record, menggunakan nilai terkini dari field dan OFS. Untuk melakukan ini, gunakan tugas yang tampaknya tidak berbahaya:"
$1 = $1 # force record to be reconstituted print $0 # or whatever else with $0
"Ini memaksa canggung untuk membangun kembali rekaman itu."
Input tes termasuk beberapa baris dengan panjang yang sama:
sumber
cat $@
rusak juga. Anda pasti ingin mengutipnya, seperticat "$@"
The solusi AWK dari neillb besar jika Anda benar-benar ingin menggunakan
awk
dan itu menjelaskan mengapa itu merepotkan sana, tetapi jika apa yang Anda inginkan adalah untuk mendapatkan pekerjaan yang dilakukan dengan cepat dan tidak peduli apa yang Anda lakukan dalam, salah satu solusi adalah dengan menggunakansort()
Fungsi Perl dengan rutinitas perbandingan kustom untuk melakukan iterasi melalui baris masukan. Ini satu baris:perl -e 'print sort { length($a) <=> length($b) } <>'
Anda dapat meletakkan ini di pipeline Anda di mana pun Anda membutuhkannya, baik menerima STDIN (dari
cat
atau shell redirect) atau cukup berikan nama file ke perl sebagai argumen lain dan biarkan membuka file.Dalam kasus saya, saya membutuhkan garis terpanjang terlebih dahulu, jadi saya bertukar
$a
dan$b
dalam perbandingan.sumber
cat testfile.txt | perl -e 'print sort { length($a) <=> length($b) } <>' > out.txt
type testfile.txt | perl -e "print sort { length($a) <=> length($b) } <>" > out.txt
Coba perintah ini sebagai gantinya:
awk '{print length, $0}' your-file | sort -n | cut -d " " -f2-
sumber
Hasil benchmark
Di bawah ini adalah hasil benchmark di seluruh solusi dari jawaban lain untuk pertanyaan ini.
Metode pengujian
Hasil
perl
Solusi Caleb membutuhkan waktu 11,2 detikperl
solusi saya membutuhkan waktu 11,6 detikawk
solusi # 1 mengambil 20 detikawk
solusi # 2 mengambil 23 detikawk
solusi anubhava membutuhkan waktu 24 detikawk
Solusi Jonathan membutuhkan waktu 25 detikbash
Solusi Fretz membutuhkan waktu 400x lebih lama dariawk
solusi (menggunakan kasus uji terpotong 100000 baris). Ini berfungsi dengan baik, hanya membutuhkan waktu selamanya.perl
Solusi lainperl -ne 'push @a, $_; END{ print sort { length $a <=> length $b } @a }' file
sumber
Bash murni:
declare -a sorted while read line; do if [ -z "${sorted[${#line}]}" ] ; then # does line length already exist? sorted[${#line}]="$line" # element for new length else sorted[${#line}]="${sorted[${#line}]}\n$line" # append to lines with equal length fi done < data.csv for key in ${!sorted[*]}; do # iterate over existing indices echo -e "${sorted[$key]}" # echo lines with equal length done
sumber
The
length()
Fungsi tidak termasuk spasi. Saya hanya akan membuat sedikit penyesuaian pada pipeline Anda (termasuk menghindari UUOC ).awk '{ printf "%d:%s\n", length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]*://'
The
sed
perintah langsung menghilangkan angka dan usus ditambah denganawk
perintah. Cara lainnya, jauhkan pemformatan Anda dariawk
:awk '{ print length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]* //'
sumber
Saya menemukan solusi ini tidak akan berfungsi jika file Anda berisi baris yang dimulai dengan angka, karena akan diurutkan secara numerik bersama dengan semua baris yang dihitung. Solusinya adalah untuk memberikan
sort
yang-g
(umum-numerik-macam) bendera bukan-n
(numerik-macam):awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2-
sumber
-n
Anda sarankan-g
untuk menghasilkan peningkatan, jadi saya harap tidak. Sekarang saya telah membahas, dalam jawaban saya, bagaimana melarang sub-penyortiran garis yang sama panjangnya (menggunakan--stable
). Apakah itu yang Anda maksud atau tidak, terima kasih telah menyampaikannya kepada saya! Saya juga telah menambahkan masukan yang dipertimbangkan untuk diuji.awk
bagian tersebut yang akan menghasilkan daftar garis yang diawali dengan panjang garis dan spasi. Menyalurkannya kesort -n
akan berfungsi seperti yang diharapkan. Tetapi jika salah satu dari garis tersebut sudah memiliki angka di awal, garis tersebut akan dimulai dengan panjang + spasi + angka.sort -n
mengabaikan spasi itu dan akan memperlakukannya sebagai satu angka yang digabungkan dari panjang + angka. Penggunaan-g
bendera malah akan berhenti di ruang pertama, menghasilkan urutan yang benar. Cobalah sendiri dengan membuat file dengan beberapa baris berawalan angka dan jalankan perintah langkah demi langkah.sort -n
mengabaikan ruang dan menghasilkan penyortiran yang salah.sort -g
mengeluarkan urutan yang benar.-n
dalamsort (GNU coreutils) 8.21
. Theinfo
dokumentasi menggambarkan-g
kurang efisien dan berpotensi kurang tepat (itu mengkonversi angka untuk mengapung), jadi mungkin tidak menggunakannya jika Anda tidak perlu.-n
: "Urutkan secara numerik. Nomor dimulai setiap baris dan terdiri dari opsional kosong, tanda '-' opsional, dan nol atau lebih digit yang mungkin dipisahkan oleh ribuan pemisah, secara opsional diikuti dengan karakter titik desimal dan nol atau lebih digit . Angka kosong dianggap sebagai '0'. Lokal 'LC_NUMERIC' menentukan karakter titik desimal dan pemisah ribuan. Secara default, kosong adalah spasi atau tab, tetapi lokal 'LC_CTYPE' dapat mengubahnya. "Dengan POSIX Awk:
{ c = length m[c] = m[c] ? m[c] RS $0 : $0 } END { for (c in m) print m[c] }
Contoh
sumber
1) solusi awk murni. Misalkan panjang garis tidak boleh lebih> 1024
nama file kucing | awk 'BEGIN {min = 1024; s = "";} {l = panjang ($ 0); jika (l <min) {min = l; s = $ 0;}} SELESAI {print s} '
2) solusi satu liner bash dengan asumsi semua baris hanya memiliki 1 kata, tetapi dapat dikerjakan ulang untuk kasus apa pun di mana semua baris memiliki jumlah kata yang sama:
BARIS = $ (nama file kucing); untuk k di $ LINES; lakukan printf "$ k"; echo $ k | wc -L; selesai | urutkan -k2 | kepala -n 1 | cut -d "" -f1
sumber
Berikut adalah metode yang kompatibel dengan multibyte untuk mengurutkan baris berdasarkan panjang. Ini membutuhkan:
wc -m
tersedia untuk Anda (macOS memilikinya).LC_ALL=UTF-8
. Anda dapat mengatur ini baik di .bash_profile Anda, atau cukup dengan membuatnya terlebih dahulu sebelum perintah berikut.testfile
memiliki pengkodean karakter yang cocok dengan lokal Anda (misalnya, UTF-8).Berikut perintah lengkapnya:
cat testfile | awk '{l=$0; gsub(/\047/, "\047\"\047\"\047", l); cmd=sprintf("echo \047%s\047 | wc -m", l); cmd | getline c; close(cmd); sub(/ */, "", c); { print c, $0 }}' | sort -ns | cut -d" " -f2-
Menjelaskan bagian demi bagian:
l=$0; gsub(/\047/, "\047\"\047\"\047", l);
← membuat salinan setiap baris dalam variabel awkl
dan double-escapes'
sehingga baris dapat di-echo dengan aman sebagai perintah shell (\047
adalah tanda kutip tunggal dalam notasi oktal).cmd=sprintf("echo \047%s\047 | wc -m", l);
← ini adalah perintah yang akan kita jalankan, yang menggemakan baris yang di-escapewc -m
.cmd | getline c;
← menjalankan perintah dan menyalin nilai jumlah karakter yang dikembalikan ke variabel awkc
.close(cmd);
← tutup pipa ke perintah shell untuk menghindari mencapai batas sistem pada jumlah file yang terbuka dalam satu proses.sub(/ */, "", c);
← memotong spasi dari nilai jumlah karakter yang dikembalikan olehwc
.{ print c, $0 }
← mencetak nilai jumlah karakter baris, spasi, dan baris asli.| sort -ns
← mengurutkan baris (dengan menambahkan nilai jumlah karakter) secara numerik (-n
), dan mempertahankan urutan pengurutan yang stabil (-s
).| cut -d" " -f2-
← menghapus nilai jumlah karakter yang ditambahkan.Ini lambat (hanya 160 baris per detik pada Macbook Pro yang cepat) karena harus menjalankan sub-perintah untuk setiap baris.
Atau, lakukan ini hanya dengan
gawk
(pada versi 3.1.5, gawk adalah multibyte aware), yang akan jauh lebih cepat. Banyak kesulitan melakukan semua pelarian dan kutipan ganda untuk melewati baris dengan aman melalui perintah shell dari awk, tetapi ini adalah satu-satunya metode yang saya temukan yang tidak memerlukan penginstalan perangkat lunak tambahan (gawk tidak tersedia secara default di macOS).sumber
menggunakan Raku (sebelumnya dikenal sebagai Perl6)
~$ cat "BinaryAve.txt" | raku -e 'given lines() {.sort(*.chars).join("\n").say};' AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Atlantis,RI,12345,(999)123-5555,1.56 AS2345,ASDF1232, Mr. Plain Example, 110 Ternary ave.,Some City,RI,12345,(999)123-5555,1.56 AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Liberty City,RI,12345,(999)123-5555,1.56 AS2345,ASDF1232, Mrs. Plain Example, 1121110 Ternary st. 110 Binary ave..,Atlantis,RI,12345,(999)123-5555,1.56
Untuk membalik urutan, tambahkan
.reverse
di tengah rantai panggilan metode - segera setelahnya.sort()
. Berikut kode yang menunjukkan yang.chars
mencakup spasi:~$ cat "number_triangle.txt" | raku -e 'given lines() {.map(*.chars).say};' (1 3 5 7 9 11 13 15 17 19 0) ~$ cat "number_triangle.txt" 1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 1 2 3 4 5 6 1 2 3 4 5 6 7 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 0
Berikut perbandingan waktu antara awk dan Raku menggunakan file txt 9,1MB dari Genbank:
~$ time cat "rat_whole_genome.txt" | raku -e 'given lines() {.sort(*.chars).join("\n").say};' > /dev/null real 0m1.308s user 0m1.213s sys 0m0.173s ~$ #awk code from neillb ~$ time cat "rat_whole_genome.txt" | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2- > /dev/null real 0m1.189s user 0m1.170s sys 0m0.050s
HTH.
https://raku.org
sumber