Saya memiliki garis (atau banyak garis) angka yang dibatasi oleh karakter arbitrer. Alat UNIX apa yang dapat saya gunakan untuk mengurutkan item setiap baris secara numerik, mempertahankan pembatas?
Contohnya termasuk:
- daftar angka; masukan
10 50 23 42
:; diurutkan:10 23 42 50
- Alamat IP; masukan
10.1.200.42
:; diurutkan:1.10.42.200
- CSV; masukan
1,100,330,42
:; diurutkan:1,42,100,330
- dibatasi pipa; masukan
400|500|404
:; diurutkan:400|404|500
Karena pembatas adalah arbitrer, jangan ragu untuk memberikan (atau memperluas) jawaban menggunakan pembatas satu karakter yang Anda pilih.
sort
numeric-data
Jeff Schaller
sumber
sumber
cut
mendukung pembatas sewenang-wenang dengan-d
opsinya.4,325 comma 55 comma 42,430
tidak akan terjadi, atau1.5 period 4.2
).Jawaban:
Anda dapat mencapai ini dengan:
ganti titik
.
dengan pembatas Anda.tambahkan
-u
kesort
perintah di atas untuk menghapus duplikat.atau dengan
gawk
( GNUawk
) kami dapat memproses banyak baris sementara di atas juga dapat diperpanjang:ganti
*
sebagai pemisah bidangSEP='*'
dengan pembatas Anda .Catatan:
Anda mungkin perlu menggunakan
-g, --general-numeric-sort
opsisort
alih - alih-n, --numeric-sort
menangani kelas angka apa pun (bilangan bulat, float, ilmiah, heksadesimal, dll).Dalam
awk
tidak ada perubahan kebutuhan, masih akan menangani mereka.sumber
Menggunakan
perl
ada versi yang jelas; pisahkan data, sortir, gabungkan kembali.Pembatas perlu didaftar dua kali (sekali dalam
split
dan sekali dalamjoin
)misalnya untuk a
,
Begitu
Karena itu
split
adalah regex, karakter mungkin perlu mengutip:Dengan menggunakan opsi
-a
dan-F
, dimungkinkan untuk menghapus pemisahan. Dengan-p
loop, seperti sebelumnya dan atur hasilnya$_
, yang secara otomatis akan mencetak:sumber
-l
opsi alih-alih menggunakanchomp
. Itu juga menambah kembali baris baru saat dicetak. Lihat juga-a
(dengan-F
) untuk bagian pemisahan.-l
dan-F
, itu bahkan lebih baik:perl -F'/\./' -le 'print join(".", sort {$a <=> $b} @F)'
-l
pilihannya; Aku merindukan itu!-F
flag karena tidak bekerja dengan baik di semua versi (misalnya baris Anda di CentOS 7 - perl 5.16.3 - mengembalikan output kosong, meskipun berfungsi dengan baik pada Debian 9). Tetapi dikombinasikan dengan-p
itu memberikan hasil yang sedikit lebih kecil, jadi saya menambahkan itu sebagai alternatif untuk jawabannya. menunjukkan bagaimana-F
bisa digunakan. Terima kasih!-a
dan-n
pilihan ketika-F
digunakan dan-n
ketika-a
digunakan ... jadi hanya mengubah-le
ke-lane
Menggunakan Python dan ide serupa seperti pada jawaban Stephen Harris :
Jadi sesuatu seperti:
Sayangnya harus melakukan I / O secara manual membuat ini jauh lebih elegan daripada versi Perl.
sumber
Skrip bash:
Contoh:
Berdasarkan
Pisahkan string ke dalam array di Bash
Cara mengurutkan array di Bash
Bergabung dengan elemen array?
sumber
Kulit
Memuat bahasa tingkat yang lebih tinggi membutuhkan waktu.
Untuk beberapa baris, shell itu sendiri bisa menjadi solusi.
Kita dapat menggunakan perintah eksternal
sort
, dan dari perintahtr
. Satu cukup efisien dalam menyortir garis dan yang lainnya efektif untuk mengubah satu pembatas menjadi baris baru:Ini perlu bash karena penggunaannya
<<<
saja. Jika itu diganti dengan di sini-doc, solusinya berlaku untuk posix.Hal ini dapat mengurutkan bidang dengan tab, spasi atau karakter shell gumpal (
*
,?
,[
). Bukan baris baru karena setiap baris sedang diurutkan.Ubah
<<<"$2"
untuk<"$2"
memproses nama file dan menyebutnya seperti:Pembatas adalah sama untuk seluruh file. Jika itu adalah batasan, itu bisa diperbaiki.
Namun file dengan hanya 6000 baris membutuhkan waktu 15 detik untuk diproses. Sungguh, shell bukanlah alat terbaik untuk memproses file.
Awk
Untuk lebih dari beberapa baris (lebih dari beberapa 10-an) lebih baik menggunakan bahasa pemrograman nyata. Solusi awk bisa berupa:
Yang hanya membutuhkan 0,2 detik untuk file 6000 baris yang sama yang disebutkan di atas.
Memahami bahwa
<"$2"
file untuk dapat diubah kembali ke<<<"$2"
untuk baris di dalam variabel shell.Perl
Solusi tercepat adalah perl.
Jika Anda ingin mengurutkan perubahan file secara
<<<"$a"
sederhana"$a"
dan menambahkan-i
ke opsi perl untuk membuat edisi file "di tempat":sumber
Menggunakan
sed
untuk mengurutkan oktet dari alamat IPsed
tidak memilikisort
fungsi bawaan, tetapi jika data Anda cukup dibatasi dalam jangkauan (seperti dengan alamat IP), Anda dapat membuat skrip sed yang secara manual mengimplementasikan semacam gelembung sederhana . Mekanisme dasarnya adalah untuk mencari nomor yang berdekatan yang rusak. Jika nomornya tidak sesuai pesanan, tukar.The
sed
Script itu sendiri berisi dua perintah pencarian dan-swap untuk setiap pasangan out-of-order nomor: satu untuk dua pasang pertama oktet (memaksa pembatas tertinggal untuk hadir untuk menandai akhir dari oktet ketiga), dan kedua untuk pasangan ketiga oktet (diakhiri dengan EOL). Jika swap terjadi, program bercabang ke bagian atas skrip, mencari angka-angka yang rusak. Kalau tidak, ia keluar.Script yang dihasilkan adalah, sebagian:
Pendekatan ini mengkodekan periode sebagai pembatas, yang harus diloloskan, karena jika tidak maka akan menjadi "spesial" untuk sintaks ekspresi reguler (memungkinkan karakter apa pun).
Untuk menghasilkan skrip sed, loop ini akan melakukan:
Redirect output skrip itu ke file lain, misalnya
sort-ips.sed
.Kemudian contoh dijalankan dapat terlihat seperti:
Variasi berikut pada skrip pembuat menggunakan penanda kata batas
\<
dan\>
untuk menghilangkan kebutuhan substitusi kedua. Ini juga mengurangi ukuran skrip yang dihasilkan dari 1,3 MB menjadi hanya di bawah 900 KB bersama dengan sangat mengurangi waktu menjalankansed
sendiri (menjadi sekitar 50% -75% dari aslinya, tergantung pada apased
implementasi yang digunakan):sumber
sed
konyol, itulah sebabnya ini merupakan tantangan yang menarik.Di sini beberapa bash yang menebak pembatas dengan sendirinya:
Mungkin tidak terlalu efisien atau bersih tetapi berfungsi.
Gunakan seperti
bash my_script.sh "00/00/18/29838/2"
.Mengembalikan kesalahan ketika pembatas yang sama tidak digunakan secara konsisten atau ketika dua atau lebih pembatas saling mengikuti.
Jika pembatas yang digunakan adalah karakter khusus, maka ia akan keluar (jika tidak
sed
mengembalikan kesalahan).sumber
Jawaban ini didasarkan pada kesalahpahaman tentang Q., tetapi dalam beberapa kasus itu tetap benar. Jika input seluruhnya bilangan alami , dan hanya memiliki satu pembatas per-baris, (seperti dengan sampel data dalam Q.), ia bekerja dengan benar. Ini juga akan menangani file dengan garis yang masing-masing memiliki pembatas sendiri, yang sedikit lebih dari apa yang diminta.
Shell ini berfungsi
read
dari input standar, menggunakan substitusi parameter POSIX untuk menemukan pembatas khusus pada setiap baris, (disimpan dalam$d
), dan digunakantr
untuk mengganti$d
dengan baris baru\n
dansort
data baris itu, kemudian mengembalikan pembatas asli setiap baris:Diterapkan pada data yang diberikan dalam OP :
Keluaran:
sumber
Untuk pembatas yang berubah-ubah:
Pada input seperti:
Memberikan:
sumber
Ini harus menangani pembatas non-digit (0-9). Contoh:
Keluaran:
sumber
Dengan
perl
:Dengan
ruby
, yang agak mirip denganperl
Perintah kustom dan hanya melewati string pembatas (bukan regex). Akan berfungsi jika input memiliki data mengambang juga
Perintah khusus untuk
perl
Bacaan lebih lanjut - Saya sudah memiliki daftar ini perl / ruby one-liners
sumber
Berikut ini adalah variasi pada jawaban Jeff dalam arti bahwa ia menghasilkan
sed
skrip yang akan melakukan semacam Bubble, tetapi cukup berbeda untuk menjamin jawabannya sendiri.Perbedaannya adalah bahwa alih-alih menghasilkan O (n ^ 2) ekspresi reguler dasar, ini menghasilkan O (n) perluasan ekspresi reguler. Script yang dihasilkan akan sekitar 15 KB besar. Waktu menjalankan
sed
skrip dalam sepersekian detik (dibutuhkan waktu lebih lama untuk menghasilkan skrip).Ini dibatasi untuk mengurutkan bilangan bulat positif yang dibatasi oleh titik-titik, tetapi tidak terbatas pada ukuran bilangan bulat (hanya meningkatkan
255
loop utama), atau jumlah bilangan bulat. Pembatas dapat diubah dengan mengubahdelim='.'
kode.Ini dilakukan kepala saya untuk mendapatkan ekspresi reguler yang benar, jadi saya akan pergi menjelaskan detail untuk hari lain.
Script akan terlihat seperti ini:
Gagasan di balik ekspresi reguler yang dihasilkan adalah untuk mencocokkan pola untuk angka yang kurang dari setiap bilangan bulat; dua nomor itu akan rusak, dan begitu juga ditukar. Ekspresi reguler dikelompokkan ke dalam beberapa opsi ATAU. Perhatikan kisaran yang ditambahkan ke setiap item, kadang-kadang
{0}
, yang berarti item segera-sebelumnya harus dihilangkan dari pencarian. Opsi regex, dari kiri ke kanan, mencocokkan nomor yang lebih kecil dari angka yang diberikan oleh:Untuk menguraikan contoh, ambil
101
(dengan ruang tambahan untuk dibaca):Di sini, pergantian pertama memungkinkan angka 100 hingga 100; pergantian kedua memungkinkan 0 hingga 99.
Contoh lain adalah
154
:Di sini opsi pertama memungkinkan 150 hingga 153; yang kedua memungkinkan 100 hingga 149, dan yang terakhir memungkinkan 0 hingga 99.
Menguji empat kali dalam satu lingkaran:
Keluaran:
sumber
Membagi input menjadi beberapa baris
Dengan menggunakan
tr
, Anda dapat membagi input menggunakan pembatas acak menjadi beberapa baris.Input ini kemudian dapat dijalankan
sort
(menggunakan-n
jika inputnya numerik).Jika Anda ingin mempertahankan pembatas di output, Anda dapat menggunakan
tr
lagi untuk menambahkan kembali pembatas.mis menggunakan ruang sebagai pembatas
cat input.txt | tr " " "\n" | sort -n | tr "\n" " "
input:
1 2 4 1 4 32 18 3
keluaran:1 1 2 3 4 4 18 32
sumber