Saya menulis skrip shell, menggunakan perintah UNIX umum. Saya harus mengambil baris yang memiliki karakter paling sedikit (termasuk spasi). Mungkin ada hingga sekitar 20 baris.
Saya tahu saya dapat menggunakan head -$L | tail -1 | wc -m
untuk menemukan jumlah karakter dari garis L. Masalahnya adalah, satu-satunya metode yang dapat saya pikirkan, menggunakan itu, akan secara manual menulis kekacauan jika pernyataan, membandingkan nilai-nilai.
Contoh data:
seven/7
4for
8 eight?
five!
Akan kembali 4for
karena garis itu memiliki karakter paling sedikit.
Dalam kasus saya, jika beberapa baris memiliki panjang terpendek, satu baris harus dikembalikan. Tidak masalah yang mana yang dipilih, asalkan panjangnya minimum. Tapi saya tidak melihat ada salahnya menampilkan kedua cara untuk pengguna lain dengan situasi lain.
sumber
Jawaban:
Cara Perl. Perhatikan bahwa jika ada banyak garis yang sama, panjang terpendek, pendekatan ini hanya akan mencetak salah satunya:
Penjelasan
perl -lne
:-n
berarti "membaca file input baris demi baris",-l
menyebabkan baris baru terhapus dari setiap baris input dan baris baru ditambahkan ke setiapprint
panggilan; dan-e
skrip yang akan diterapkan untuk setiap baris.$m//=$_
: disetel$m
ke baris saat ini ($_
) kecuali$m
ditentukan. The//=
operator adalah tersedia sejak Perl 5.10.0.$m=$_ if length()<length($m)
: jika panjang nilai saat$m
ini lebih besar dari panjang garis saat ini, simpan garis saat ini ($_
) sebagai$m
.END{print $m if $.}
: setelah semua baris diproses, cetak nilai saat ini dari$m
, garis terpendek. Yangif $.
memastikan bahwa ini hanya terjadi ketika nomor baris ($.
) ditentukan, menghindari mencetak baris kosong untuk input kosong.Atau, karena file Anda cukup kecil untuk muat dalam memori, Anda dapat melakukan:
Penjelasan
@K=sort{length($a) <=> length($b)}<>
: di<>
sini adalah array yang elemen-elemennya adalah baris-baris file. Thesort
akan mengurutkan mereka sesuai dengan panjang mereka dan garis diurutkan disimpan sebagai array yang@K
.print "$K[0]"
: cetak elemen pertama array@K
: baris terpendek.Jika Anda ingin mencetak semua garis terpendek, Anda dapat menggunakan
sumber
-C
untuk mengukur panjang dalam hal jumlah karakter, bukan jumlah byte. Di lokal UTF-8,$$
memiliki byte lebih sedikit dari€
(2 vs 3), tetapi lebih banyak karakter (2 vs 1).Dengan
sqlite3
:sumber
strace
menunjukkan). Jika Anda perlu bekerja dengan file yang sangat besar (dan sistem Anda tidak bertukar), Anda dapat memaksanya dengan hanya menambahkan nama file sepertisqlite3 $(mktemp)
dan semua data akan ditulis ke disk.Berikut varian
awk
solusi untuk mencetak garis minimum pertama yang ditemukan:yang dapat diperpanjang dengan satu syarat untuk mencetak semua baris minimum:
sumber
Python keluar cukup ringkas, dan kode Melakukan Apa Yang Dikatakan Pada Timah:
python -c "import sys; print min(sys.stdin, key=len),"
Saya akui bahwa koma terakhir tidak jelas. Ini mencegah pernyataan cetak menambahkan linebreak tambahan. Selain itu, Anda dapat menulis ini dalam Python 3 yang mendukung 0 baris seperti:
python3 -c "import sys; print(min(sys.stdin, key=len, default='').strip('\n'))"
sumber
Saya selalu menyukai solusi dengan skrip shell murni (no exec!).
Catatan :
Ada masalah dengan NUL byte di input. Jadi,
printf "ab\0\0\ncd\n" | bash this_script
cetakab
bukancd
.sumber
bash
meyakinkan saya untuk menyalurkan hasil antarasort
sebagai gantinya.var=$(get data)
karena membatasi aliran data ke satu konteks - tetapi ketika Anda memindahkan data melalui pipa - dalam aliran - setiap eksekutif yang diterapkan umumnya membantu - karena memungkinkan khusus aplikasi program modular hanya jika diperlukan.$IFS
bukan digit-diskriminatif - bahkan jika tidak ada dalam$IFS
nilai default , meskipun banyak shell akan menerima konfigurasi lingkungan preset untuk$IFS
- dan sehingga itu bukan standar yang dapat diandalkan./bin/sh
yang tersedia. Itu terjadi pada saya beberapa kali dengan host SunOS4 yang/usr
hilang atau.so
rusak, dan sekarang di zaman Linux modern saya kadang-kadang masih menghadapi situasi yang sama dengan embedded system atau initrd dari sistem boot gagal. BusyBox adalah salah satu hal hebat yang baru-baru ini kami peroleh.Di sini
zsh
solusi murni (mencetak semua garis dengan panjang minimal, darifile
):Input contoh:
Output adalah:
Saya pikir perlu penjelasan singkat :-)
Pertama, kami mengatur pemisah bidang internal ke baris baru:
Sejauh ini bagus, sekarang bagian yang sulit.
print
menggunakan-l
bendera untuk mencetak hasil yang dipisahkan oleh baris baru, bukan spasi.Sekarang, kita mulai dari dalam:
File dibaca baris demi baris dan diperlakukan sebagai array. Kemudian:
The
o
flag mengatakan bahwa hasilnya harus dipesan dalam urutan menaik, yang@
berarti untuk mengobati hasil sebagai array yang terlalu. Bagian di belakang (//?/?
) adalah pengganti yang menggantikan semua karakter dengan a?
. Sekarang:Kami mengambil elemen array pertama
[1]
, yang merupakan yang terpendek, dalam kasus Anda sekarang????
.Pencocokan dilakukan pada setiap elemen array secara terpisah, dan elemen array yang tidak cocok akan dihapus (
M
). Setiap elemen yang cocok????
(4 karakter) tetap dalam array. Jadi elemen yang tersisa adalah yang memiliki 4 karakter (yang terpendek).Sunting: Jika Anda hanya perlu satu baris terpendek, versi modifikasi ini mencetak yang pertama:
sumber
... dan pemenangnya adalah ... baris 2, sepertinya.
Tetapi masalah dengan itu adalah bahwa setiap baris harus lebih dari dua kali panjang agar bisa berfungsi - jadi LINE_MAX dibelah dua secara efektif. Penyebabnya adalah bahwa ia menggunakan - apa, basis 1? - untuk merepresentasikan panjang garis. Pendekatan serupa - dan mungkin lebih rapi - mungkin untuk mengompres informasi tersebut dalam aliran. Gagasan pertama yang muncul dalam benak saya adalah bahwa saya harus melakukannya
unexpand
:Cetakan itu ...
Satu lagi, hanya
sed
:Sintaksnya sesuai standar - tapi itu bukan jaminan bahwa yang lama
sed
akan menangani dengan\(reference-group\)\{counts\}
benar - banyak yang tidak.Ini pada dasarnya menerapkan regexp yang sama untuk memasukkan berulang kali - yang bisa sangat bermanfaat ketika saatnya untuk mengkompilasi mereka. Pola itu adalah:
Yang cocok dengan string berbeda dengan cara yang berbeda. Sebagai contoh:
... cocok dengan
s
di\1
dan''
string nol di\2
.... cocok dengan
1
di\1
dan\nstring2\nstring3
di\2
... cocok dengan
\n
di\1
dan''
string nol di\2
. Ini akan bermasalah jika ada peluang\n
ewline terjadi di kepala ruang pola - tetapi/^\n/D
, dan//!g
perintah digunakan untuk mencegah hal ini. Saya memang menggunakan[^\n]
tetapi kebutuhan lain untuk skrip kecil ini membuat portabilitas menjadi perhatian dan saya tidak puas dengan banyak cara yang sering disalahartikan. Plus,.
lebih cepat.... cocokkan
\n
dans
lagi\1
dan keduanya mendapatkan''
string nol\2
. Baris kosong tidak cocok sama sekali.Ketika pola diterapkan secara
g
lobal , dua bias - baik bias kiri paling standar maupun bias sisi kanan lebih rendah\n
- berlawanan arah untuk menghasilkan lompatan. Beberapa contoh:... jika semua diterapkan (tidak berturut-turut) ke string berikut ...
... akan mengubahnya menjadi ...
Pada dasarnya saya menggunakan regexp untuk selalu hanya menangani baris pertama di setiap pola-ruang yang saya terapkan. Itu memungkinkan saya untuk menyulap dua versi yang berbeda dari kedua jalur yang terpendek-cocok-sejauh-tetap dan yang terbaru tanpa menggunakan loop tes - setiap penggantian yang diterapkan menangani seluruh ruang pola sekaligus.
Versi yang berbeda diperlukan untuk perbandingan string / string literal - jadi harus ada versi setiap baris di mana semua karakter dijamin sama. Tetapi tentu saja jika salah satu dari yang lain benar-benar berakhir menjadi jalur input terpendek yang muncul, maka jalur yang dicetak ke output mungkin harus merupakan versi asli dari jalur tersebut - bukan versi yang telah saya sanitasi / dihomogenisasi untuk kepentingan perbandingan. Jadi saya butuh dua versi masing-masing.
Sangat disayangkan bahwa kebutuhan lain adalah banyak switching buffer untuk menangani yang sama - tetapi setidaknya tidak ada buffer yang melebihi lebih dari empat baris yang diperlukan untuk tetap terkini - dan jadi mungkin itu tidak mengerikan.
Bagaimanapun, untuk setiap siklus, hal pertama yang terjadi adalah transformasi pada baris yang diingat - karena satu-satunya salinan yang benar-benar disimpan adalah yang asli - ke ...
... dan sesudahnya jalur
n
input ext menimpa buffer lama. Jika tidak mengandung setidaknya satu karakter, maka secara efektif diabaikan. Jauh lebih mudahq
menggunakan baris kosong pertama, tetapi, well, data pengujian saya memiliki banyak hal dan saya ingin menangani beberapa paragraf.Dan jika itu memang mengandung karakter, versi literalnya ditambahkan ke baris yang diingat dan versi perbandingannya ditempatkan di kepala ruang pola, seperti ini:
Terakhir substitusi diterapkan ke ruang pola itu:
Jadi, jika baris baru dapat masuk dalam ruang yang diperlukan untuk memuat baris yang diingat dengan setidaknya satu karakter cadangan, maka dua baris pertama diganti, yang lain hanya yang pertama.
Terlepas dari hasilnya, baris pertama dalam ruang pola selalu
D
dihilangkan pada akhir siklus sebelum memulai lagi. Ini berarti bahwa jika baris baru lebih pendek dari yang terakhir ...... dikirim kembali ke subtitusi pertama dalam siklus yang akan selalu dihapus hanya dari karakter baris baru pertama - dan tetap utuh. Tetapi jika tidak maka string ...
... akan memulai siklus berikutnya sebagai gantinya, dan substitusi pertama akan menghapus darinya string ...
...setiap saat.
Pada baris terakhir baris yang diingat dicetak ke standar keluar, dan jadi untuk contoh data yang diberikan, ia mencetak:
Tapi, serius, gunakan
tr
.sumber
REINPUT | sort -t: -nk1,1 | cut -d: -f3-
. Dan yang kedua adalah masalah sederhana termasuksed
--expression
naskah lain di bagian ekor.sort
perilaku sebagai tie-breaker ketika garis dengan panjang yang sama terjadi pada input - sehingga garis yang muncul paling awal selalu mengapung ke atas dalam kasus itu.Mencoba:
Idenya adalah menggunakan
awk
untuk mencetak panjang setiap baris terlebih dahulu. Ini akan muncul sebagai:Kemudian, gunakan jumlah karakter untuk mengurutkan garis
sort
,cut
untuk menghilangkan jumlah danhead
untuk menjaga baris pertama (yang memiliki karakter paling sedikit). Anda tentu saja dapat menggunakantail
untuk mendapatkan garis dengan karakter terbanyak dalam kasus ini.(Ini diadopsi dari jawaban ini )
sumber
head -1
tail
(karenahead
dapat keluar segera setelah tugasnya selesai, tanpa membaca sisa inputnya).Dengan POSIX awk:
sumber
L
adalah surat terbaik untuk memilih untuk menyebutkan variabel: D Sesuatu sepertimin
akan membuat segalanya lebih jelasMeminjam beberapa gagasan @ mikeserv:
Yang pertama
sed
melakukan yang berikut:h
menyimpan baris asli ke buffer penahan:
- ini untuk menghilangkan bahaya injeksi kodeexpr length "whole line"
- ini adalah ekspresi shell yang dapat dievaluasis
adalah ekstensi sed GNU untuk mengevaluasi ruang pola dan mengembalikan hasilnya ke dalam ruang pola.G
menambahkan baris baru dan isi ruang pegang (garis asli) ke ruang polas
menggantikan baris baru dengan tabJumlah karakter sekarang menjadi angka di awal setiap baris, jadi
sort -n
urutkan berdasarkan panjang garis.Final
sed
kemudian menghapus semua kecuali baris pertama (terpendek) dan panjang garis dan mencetak hasilnya.sumber
expr
lebih baik di sini. Ya,e
akan menelurkan shell untuk setiap baris. Saya mengedit ekspresi sed sehingga menggantikan masing-masing char di string dengan:
sebelum eval yang menurut saya harus menghapus segala kemungkinan injeksi kode.xargs expr
pribadi - tetapi, selain menghindari shell perantara, itu mungkin lebih merupakan hal gaya. Lagipula aku menyukainya.Terpikir olehku bahwa semuanya mungkin dalam satu
sed
ekspresi. Itu tidak cantik:Hancurkan ini:
BSD sed di OS X sedikit lebih rewel dengan baris baru. Versi ini berfungsi untuk versi sed BSD dan GNU:
Perhatikan bahwa ini lebih merupakan jawaban "karena mungkin" daripada upaya serius untuk memberikan jawaban praktik terbaik. Saya kira itu berarti saya telah bermain terlalu banyak kode-colf
sumber
man sed
pada OS X: "Urutan escape \ n cocok dengan karakter baris baru yang tertanam dalam ruang pola" . Jadi saya pikir GNU sed memungkinkan\n
di regex dan penggantian, sedangkan BSD hanya memungkinkan\n
di regex dan tidak di penggantian.\n
dari ruang pola adalah ide yang bagus dan akan bekerja padas///
ekspresi kedua , tetapis/.*/&\n&/
ekspresi tersebut memasukkan a\n
ke dalam ruang pola di mana sebelumnya tidak ada. BSD juga tampaknya membutuhkan baris baru literal setelah definisi label dan cabang.sed
skrip harus berupa file teks kecuali bahwa skrip tersebut tidak harus diakhiri dengan baris baru . Jadi Anda biasanya dapat membatasi mereka sebagai argumen yang terpisah juga -sed -e :\ label -e :\ label2
dan seterusnya. Karena Anda1h
tetap melakukannya , Anda bisa saja beralih ke beberapa logika berdasarkanx;H
untuk mendapatkan baris baru Anda - dan Anda dapat memotong baris baru dari ruang pola di akhir siklus tanpa menarik baris baru dengan /D
.G
pertama dan mengubahs///
ekspresi. Memisahkannya menggunakan-e
memungkinkan semuanya berjalan pada satu (panjang) baris tanpa baris baru.\n
keluarnya juga ditentukan untuksed
LHS, dan saya pikir itu adalah pernyataan spec secara verbatim, kecuali bahwa ekspresi kurung POSIX juga ditentukan sedemikian rupa sehingga semua karakter kehilangan arti khusus mereka - (termasuk secara eksplisit\\
) - dalam satu kecuali tanda kurung, tanda hubung sebagai pemisah rentang, dan titik, sama, tanda sisipan, titik dua untuk kolasi, ekuivalensi, negasi, dan kelas.Solusi perl lain: menyimpan garis-garis dalam hash-of-array, kunci hash menjadi panjang garis. Kemudian, cetak garis dengan kunci minimum.
sumber
push @{$lines{+length}};
danprint @{$lines{+min keys %lines}};
untuk mengetik lebih sedikit :)perl -MList::Util=min -nE'push @{$l{+length}},$_}END{say@{$l{min keys%l}}' sample
perl
Menjadi sedikit degil bagi kita yang tidak terbiasaperl
dengan sifat samar. BTW. golfsay
mencetak garis kosong palsu di bagian akhir.Untuk mendapatkan garis terpendek pertama:
Untuk mendapatkan semua serat terpendek, ubah saja
{p;q}
kep
Metode lain (agak tidak biasa) adalah
sort
melakukan sort sebenarnya berdasarkan panjangnya . Itu relatif lambat bahkan dengan garis pendek, dan menjadi lebih lambat secara dramatis ketika panjang garis meningkat.Namun, saya menemukan ide menyortir dengan tombol yang tumpang tindih cukup menarik. Saya mempostingnya kalau-kalau orang lain juga menganggapnya menarik / informatif.
Cara kerjanya:
Urutkan berdasarkan varian panjang dari kunci yang sama -
key 1
yang membentang seluruh baris.Setiap varian kunci berturut-turut menambah panjang kunci dengan satu karakter, hingga panjang baris terpanjang file (ditentukan oleh
wc -L
)Untuk mendapatkan baris terpendek pertama (diurutkan):
yang sama dengan:
sumber
Dengan asumsi garis kosong tidak dianggap sebagai garis terpendek dan garis kosong itu mungkin ada, AWK murni berikut ini akan berfungsi:
sumber
Bagaimana dengan menggunakan sortir?
sumber
Dengan GNU awk
Baca setiap baris menjadi array yang diindeks oleh panjang garis.
Setel
PROCINFO["sorted_in"]
untuk@ind_num_asc
memaksa pemindaian array agar dipesan oleh indeks array, diurutkan secara numerikPengaturan
PROCINFO
dengan cara di atas memaksa garis dengan panjang terkecil untuk diambil pertama kali dalam lintasan array. Jadi cetak elemen pertama dari array dan keluarIni memiliki kelemahan karena
nlogn
beberapa saat dari beberapa pendekatan lainn
tepat waktusumber
Metode alat shell tingkat menengah, tanpa
sed
atauawk
:sumber
$f
variabel; Saya punya gagasan yang mungkin bisa menggunakantee
entah bagaimana ...