Cara menemukan baris dengan karakter paling sedikit

22

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 -muntuk 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 4forkarena 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.

Matthew D. Scholefield
sumber
5
Bagaimana jika ada banyak garis dengan panjang 4? Haruskah mereka dicetak juga?
kekacauan
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.
Matthew D. Scholefield

Jawaban:

13

Cara Perl. Perhatikan bahwa jika ada banyak garis yang sama, panjang terpendek, pendekatan ini hanya akan mencetak salah satunya:

perl -lne '$m//=$_; $m=$_ if length()<length($m); END{print $m if $.}' file 

Penjelasan

  • perl -lne: -nberarti "membaca file input baris demi baris", -lmenyebabkan baris baru terhapus dari setiap baris input dan baris baru ditambahkan ke setiap printpanggilan; dan -eskrip yang akan diterapkan untuk setiap baris.
  • $m//=$_: disetel $mke baris saat ini ( $_) kecuali $mditentukan. The //=operator adalah tersedia sejak Perl 5.10.0.
  • $m=$_ if length()<length($m): jika panjang nilai saat $mini 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. Yang if $.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:

perl -e '@K=sort{length($a) <=> length($b)}<>; print "$K[0]"' file 

Penjelasan

  • @K=sort{length($a) <=> length($b)}<>: di <>sini adalah array yang elemen-elemennya adalah baris-baris file. The sortakan 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

perl -e '@K=sort{length($a) <=> length($b)}<>; 
         print grep {length($_)==length($K[0])}@K; ' file 
terdon
sumber
1
Tambahkan -Cuntuk 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).
Stéphane Chazelas
17

Dengan sqlite3:

sqlite3 <<EOT
CREATE TABLE file(line);
.import "data.txt" file
SELECT line FROM file ORDER BY length(line) LIMIT 1;
EOT
FloHimself
sumber
Yang itu adalah favorit saya di sini, tidak pernah memikirkan SQL ...
kekacauan
2
Ini adalah kode golf status pintar
shadowtalker
2
Apakah ini akan membaca seluruh file ke dalam memori dan / atau membuat salinan pada disk kedua? Jika demikian, itu pintar tetapi tidak efisien.
John Kugelman mendukung Monica
1
@JohnKugelman Ini mungkin akan menyerap seluruh 4 baris ke dalam basis data hanya memori sementara (itulah yang stracemenunjukkan). Jika Anda perlu bekerja dengan file yang sangat besar (dan sistem Anda tidak bertukar), Anda dapat memaksanya dengan hanya menambahkan nama file seperti sqlite3 $(mktemp)dan semua data akan ditulis ke disk.
FloHimself
Saya mendapatkan kesalahan berikut: "" "xaa: 8146:" karakter "" "" dan "" "xaa: 8825: diharapkan 1 kolom tetapi ditemukan 2 - ekstra diabaikan" "". File ini terdiri dari dokumen json 1 per setiap baris .
Ahmedov
17

Berikut varian awksolusi untuk mencetak garis minimum pertama yang ditemukan:

awk '
  NR==1 || length<len {len=length; line=$0}
  END {print line}
'

yang dapat diperpanjang dengan satu syarat untuk mencetak semua baris minimum:

awk '
  length==len {line=line ORS $0}
  NR==1 || length<len {len=length; line=$0}
  END {print line}'
'
Janis
sumber
12

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'))"

Steve Jessop
sumber
apa yang kaleng katakan?
mikeserv
@mikeserve: dikatakan, "mencetak minimum sys.stdin, menggunakan len sebagai kuncinya" ;-)
Steve Jessop
1
ahh apa-apa tentang ukuran biner, ketergantungan creep atau waktu eksekusi, lalu?
mikeserv
2
@ mikeserv: tidak, cetakan kecil tidak ada di kaleng. Ada di selebaran penasehat di lemari arsip yang terkunci, di ruang bawah tanah, di balik pintu bertanda "Waspadalah terhadap macan tutul".
Steve Jessop
Gotcha - begitu pada layar.
mikeserv
10

Saya selalu menyukai solusi dengan skrip shell murni (no exec!).

#!/bin/bash
min=
is_empty_input="yes"

while IFS= read -r a; do
    if [ -z "$min" -a "$is_empty_input" = "yes" ] || [ "${#a}" -lt "${#min}" ]; then
        min="$a"
    fi
    is_empty_input="no"
done

if [ -n "$a" ]; then
    if [ "$is_empty_input" = "yes" ]; then
        min="$a"
        is_empty_input="no"
    else
        [ "${#a}" -lt "${#min}" ] && min="$a"
    fi
fi

[ "$is_empty_input" = "no" ] && printf '%s\n' "$min"

Catatan :

Ada masalah dengan NUL byte di input. Jadi, printf "ab\0\0\ncd\n" | bash this_scriptcetak abbukan cd.

yaegashi
sumber
Ini benar-benar yang paling murni. Meskipun, kecanggungan tes dalam bashmeyakinkan saya untuk menyalurkan hasil antara sortsebagai gantinya.
orion
2
Sudahkah Anda mencoba membuat bangku eksekutif Anda! solusi versus yang lain lakukan? Berikut ini adalah perbandingan perbedaan kinerja antara eksekutif! dan tidak ada eksekutif! solusi untuk masalah serupa. mengeksekusi proses terpisah sangat jarang menguntungkan ketika spider - dalam bentuk seperti 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.
mikeserv
1
@DigitalTrauma - string angka yang diperluas yang berdekatan tidak lebih atau kurang dibebaskan dari kondisi yang membuat pengutipan shell diperlukan daripada string lain yang diperluas. $IFSbukan digit-diskriminatif - bahkan jika tidak ada dalam $IFSnilai default , meskipun banyak shell akan menerima konfigurasi lingkungan preset untuk $IFS- dan sehingga itu bukan standar yang dapat diandalkan.
mikeserv
1
Terima kasih atas komentar dan upvotes (beberapa perwakilan harus pergi ke @cuonglm untuk mengoreksi jawaban saya). Secara umum saya tidak menyarankan orang lain untuk berlatih scripting shell murni tetapi keterampilan itu dapat ditemukan sangat berguna dalam beberapa kondisi ekstrim di mana tidak ada yang lain selain tautan statis /bin/shyang tersedia. Itu terjadi pada saya beberapa kali dengan host SunOS4 yang /usrhilang atau .sorusak, 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.
yaegashi
9

Di sini zshsolusi murni (mencetak semua garis dengan panjang minimal, dari file):

IFS=$'\n'; print -l ${(M)$(<file):#${~${(o@)$(<file)//?/?}[1]}}

Input contoh:

seven/7
4for
8 eight?
five!
four

Output adalah:

4for
four

Saya pikir perlu penjelasan singkat :-)


Pertama, kami mengatur pemisah bidang internal ke baris baru:

IFS=$'\n';

Sejauh ini bagus, sekarang bagian yang sulit. printmenggunakan -lbendera untuk mencetak hasil yang dipisahkan oleh baris baru, bukan spasi.

Sekarang, kita mulai dari dalam:

$(<file)

File dibaca baris demi baris dan diperlakukan sebagai array. Kemudian:

${(o@)...//?/?}

The oflag 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:

${~...[1]}

Kami mengambil elemen array pertama [1], yang merupakan yang terpendek, dalam kasus Anda sekarang ????.

${(M)$(<file):#...}

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:

IFS=$'\n'; print -l ${${(M)$(<file):#${~${(o@)$(<file)//?/?}[1]}}[1]}
kekacauan
sumber
8
tr -c \\n 1 <testfile |   #first transform every [^\n] char to a 1
grep -nF ''           |   #next get line numbers
paste -d: - testfile  |   #then paste it together with itself
sort  -t: -nk2,2          #then sort on second field

... dan pemenangnya adalah ... baris 2, sepertinya.

2:1111:4for
4:11111:five!
1:1111111:seven/7
3:11111111:8 eight?

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:

tr -c \\n \  <testfile    |   #transform all [^\n] to <space>
unexpand -t10             |   #squeeze every series of 10 to one tab
grep -nF ''               |   #and get the line numbers
sed    's/:/!d;=;:/;h;:big    #sed compares sequential lines
$P;$!N; /\(:[^ ]*\)\( *\)\n.*\1.*\2/!D     #newest line is shorter or...
        g;/:./!q;b big'   |   #not; quit input entirely for blank line
sed -f - -e q testfile        #print only first occurrence of shortest line

Cetakan itu ...

2
4for

Satu lagi, hanya sed:

sed -n '/^\n/D;s/\(.\)\(\n.*\)*/\1/g
$p;h;   s// /g;G;x;n;//!g;H;s// /g
G;      s/^\( *\)\(\n \1 *\)\{0,1\}\n//
D'      <infile >outfile

Sintaksnya sesuai standar - tapi itu bukan jaminan bahwa yang lama sedakan 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:

\(.\)\(\n.*\)*

Yang cocok dengan string berbeda dengan cara yang berbeda. Sebagai contoh:

string1\nstring2\nstring3

... cocok dengan sdi \1dan ''string nol di \2.

1\nstring2\nstring3

... cocok dengan 1di \1dan \nstring2\nstring3di\2

\nstring2\nstring3

... cocok dengan \ndi \1dan ''string nol di \2. Ini akan bermasalah jika ada peluang \newline terjadi di kepala ruang pola - tetapi /^\n/D, dan //!gperintah 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.

\nstring2
string1

... cocokkan \ndan slagi \1dan keduanya mendapatkan ''string nol \2. Baris kosong tidak cocok sama sekali.

Ketika pola diterapkan secara global , dua bias - baik bias kiri paling standar maupun bias sisi kanan lebih rendah \n- berlawanan arah untuk menghasilkan lompatan. Beberapa contoh:

s/\(.\)\(\n.*\)*/\1:\2/g
s/\(.\)\(\n.*\)*/\2\1:/g
s/\(.\)\(\n.*\)*/\1: /g
s/\(.\)\(\n.*\)*/ :\2/g

... jika semua diterapkan (tidak berturut-turut) ke string berikut ...

string1\nstring2

... akan mengubahnya menjadi ...

s:t:r:i:n:g:1:\nstring2
s:t:r:i:n:g:\nstring21:
s:t:r:i:n:g:1: 
 : : : : : : :\nstring2

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 ...

^               \nremembered line$

... dan sesudahnya jalur ninput 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:

^   \n               \nremembered line\nnew$

Terakhir substitusi diterapkan ke ruang pola itu:

s/^\( *\)\(\n \1 *\)\{0,1\}\n//

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 Ddihilangkan pada akhir siklus sebelum memulai lagi. Ini berarti bahwa jika baris baru lebih pendek dari yang terakhir ...

new

... 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 ...

remembered line\nnew

... akan memulai siklus berikutnya sebagai gantinya, dan substitusi pertama akan menghapus darinya string ...

\nnew

...setiap saat.

Pada baris terakhir baris yang diingat dicetak ke standar keluar, dan jadi untuk contoh data yang diberikan, ia mencetak:

4for

Tapi, serius, gunakan tr.

mikeserv
sumber
Apakah Anda bahkan perlu memasukkan nomor baris? Pembacaan OP saya adalah bahwa hanya garis terpendek yang diperlukan, dan belum tentu nomor baris dari garis itu. Saya kira tidak ada salahnya menunjukkannya untuk kelengkapan.
Trauma Digital
@DigitalTrauma - nah, mungkin tidak. Tapi itu sangat berguna tanpa mereka - dan mereka datang begitu murah. Ketika mengerjakan aliran, saya selalu lebih suka menyertakan cara mereproduksi input asli secara identik dalam output - nomor baris memungkinkan di sini. Misalnya, untuk mengubah hasil sekitar pipa pertama: REINPUT | sort -t: -nk1,1 | cut -d: -f3-. Dan yang kedua adalah masalah sederhana termasuk sed --expressionnaskah lain di bagian ekor.
mikeserv
@DigitalTrauma - oh, dan dalam contoh pertama, nomor baris benar- benar memengaruhi sortperilaku 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.
mikeserv
7

Mencoba:

awk '{ print length, $0 }' testfile | sort -n | cut -d" " -f2- | head -1

Idenya adalah menggunakan awkuntuk mencetak panjang setiap baris terlebih dahulu. Ini akan muncul sebagai:

echo "This is a line of text" | awk '{print length, $0}'
22 This is a line of text

Kemudian, gunakan jumlah karakter untuk mengurutkan garis sort, cutuntuk menghilangkan jumlah dan headuntuk menjaga baris pertama (yang memiliki karakter paling sedikit). Anda tentu saja dapat menggunakan tailuntuk mendapatkan garis dengan karakter terbanyak dalam kasus ini.

(Ini diadopsi dari jawaban ini )

Bichoy
sumber
Memberi +1 untuk logika tetapi tidak akan berfungsi dalam semua kasus. Jika dua baris memiliki jumlah karakter yang sama dan yang minimum. Ini akan memberi Anda hanya baris pertama yang ditemui karenahead -1
Thushi
Untuk mendapatkan garis terpanjang, ini sedikit lebih efisien untuk membalikkan jenis daripada menggunakan tail(karena headdapat keluar segera setelah tugasnya selesai, tanpa membaca sisa inputnya).
Toby Speight
@ Thushi Menggunakan sedikit regex, setelah mencetak nomor garis, semuanya kecuali garis dengan nomor yang sama dengan baris 1, dapat dihapus, sehingga menghasilkan semua garis terpendek.
Matthew D. Scholefield
5

Dengan POSIX awk:

awk 'FNR==1{l=$0;next};length<length(l){l=$0};END{print l}' file
cuonglm
sumber
Ini tidak akan berfungsi jika lebih dari satu baris memiliki jumlah karakter yang sama dan yang juga minimum.
Thushi
@ Thushi: Ini akan melaporkan baris minimum pertama.
cuonglm
Ya. Tapi itu bukan keluaran yang benar, kan? Bahkan baris lain memiliki jumlah karakter minimum.
Thushi
1
@ Thushi: Itu tidak menyebutkan persyaratan OP, menunggu pembaruan dari OP.
cuonglm
3
Saya tidak berpikir Ladalah surat terbaik untuk memilih untuk menyebutkan variabel: D Sesuatu seperti minakan membuat segalanya lebih jelas
fedorqui
3

Meminjam beberapa gagasan @ mikeserv:

< testfile sed 'h;s/./:/g;s/.*/expr length "&"/e;G;s/\n/\t/' | \
sort -n | \
sed -n '1s/^[0-9]+*\t//p'

Yang pertama sedmelakukan yang berikut:

  • h menyimpan baris asli ke buffer penahan
  • Ganti setiap karakter dalam baris dengan :- ini untuk menghilangkan bahaya injeksi kode
  • Ganti seluruh baris dengan expr length "whole line"- ini adalah ekspresi shell yang dapat dievaluasi
  • Perintah e tos 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 pola
  • final smenggantikan baris baru dengan tab

Jumlah karakter sekarang menjadi angka di awal setiap baris, jadi sort -nurutkan berdasarkan panjang garis.

Final sedkemudian menghapus semua kecuali baris pertama (terpendek) dan panjang garis dan mencetak hasilnya.

Trauma Digital
sumber
1
@ mikeserv Ya saya pikir exprlebih baik di sini. Ya, eakan 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.
Trauma Digital
Saya biasanya akan memilih secara xargs exprpribadi - tetapi, selain menghindari shell perantara, itu mungkin lebih merupakan hal gaya. Lagipula aku menyukainya.
mikeserv
3

Terpikir olehku bahwa semuanya mungkin dalam satu sedekspresi. Itu tidak cantik:

$ sed '1h;s/.*/&\n&/;G;:l;s/\n[^\n]\([^\n]*\)\n[^\n]/\n\1\n/;tl;/\n\n/{s/\n.*//;x};${x;p};d' testfile
4for
$ 

Hancurkan ini:

1h            # save line 1 in the hold buffer (shortest line so far)
s/.*/&\n&/    # duplicate the line with a newline in between
G             # append newline+hold buffer to current line
:l            # loop start
s/\n[^\n]\([^\n]*\)\n[^\n]/\n\1\n/
              # attempt to remove 1 char both from current line and shortest line
tl            # jump back to l if the above substitution succeeded
/\n\n/{       # matches if current line is shorter
  s/\n.*//    # remove all but original line
  x           # save new shortest line in hold buffer
}
${            # at last line
  x           # get shortest line from hold buffer
  p           # print it
}
d             # don't print any other lines

BSD sed di OS X sedikit lebih rewel dengan baris baru. Versi ini berfungsi untuk versi sed BSD dan GNU:

$ sed -e '1h;G;s/\([^\n]*\)\(\n\)\(.*\)/\1\2\1\2\3/;:l' -e 's/\(\n\)[^\n]\([^\n]*\n\)[^\n]/\1\2/;tl' -e '/\n\n/{s/\n.*//;x;};${x;p;};d' testfile
4for
$

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

Trauma Digital
sumber
@mikeserv Dari man sedpada OS X: "Urutan escape \ n cocok dengan karakter baris baru yang tertanam dalam ruang pola" . Jadi saya pikir GNU sed memungkinkan \ndi regex dan penggantian, sedangkan BSD hanya memungkinkan \ndi regex dan tidak di penggantian.
Digital Trauma
Meminjam \ndari ruang pola adalah ide yang bagus dan akan bekerja pada s///ekspresi kedua , tetapi s/.*/&\n&/ekspresi tersebut memasukkan a \nke dalam ruang pola di mana sebelumnya tidak ada. BSD juga tampaknya membutuhkan baris baru literal setelah definisi label dan cabang.
Trauma Digital
1
Baris-baris baru itu adalah pembatas parameter - Anda perlu mereka membatasi perintah apa pun yang mungkin menerima parameter arbitrer - setidaknya, itulah yang dikatakan oleh spec. Spec juga mengatakan bahwa sedskrip 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 :\ label2dan seterusnya. Karena Anda 1htetap melakukannya , Anda bisa saja beralih ke beberapa logika berdasarkan x;Huntuk mendapatkan baris baru Anda - dan Anda dapat memotong baris baru dari ruang pola di akhir siklus tanpa menarik baris baru dengan / D.
mikeserv
@ mikeserv Nice. Ya, saya memasukkan baris baru yang saya butuhkan dengan melakukan yang Gpertama dan mengubah s///ekspresi. Memisahkannya menggunakan -ememungkinkan semuanya berjalan pada satu (panjang) baris tanpa baris baru.
Digital Trauma
Jalan \nkeluarnya juga ditentukan untuk sedLHS, 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.
mikeserv
2

Solusi perl lain: menyimpan garis-garis dalam hash-of-array, kunci hash menjadi panjang garis. Kemudian, cetak garis dengan kunci minimum.

perl -MList::Util=min -ne '
    push @{$lines{ length() }}, $_;
} END {
    print @{$lines{ min keys %lines }};
' sample 
4for
glenn jackman
sumber
Anda dapat menggunakan push @{$lines{+length}};dan print @{$lines{+min keys %lines}};untuk mengetik lebih sedikit :)
cuonglm
Jika saya bermain golf, saya tidak akan menggunakan nama variabel "baris" baik:perl -MList::Util=min -nE'push @{$l{+length}},$_}END{say@{$l{min keys%l}}' sample
glenn jackman
+1 untuk versi non-golf (yang berfungsi!), Meskipun hanya untuk varian cetak semua . - perlMenjadi sedikit degil bagi kita yang tidak terbiasa perldengan sifat samar. BTW. golf saymencetak garis kosong palsu di bagian akhir.
Peter.O
2

Untuk mendapatkan garis terpendek pertama:

f=file; sed -n "/^$(sed 's/./1/g' $f | sort -ns | sed 's/././g;q')$/{p;q}" $f

Untuk mendapatkan semua serat terpendek, ubah saja {p;q}kep


Metode lain (agak tidak biasa) adalah sortmelakukan 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 1yang 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):

f=file; sort -t'\0' $(seq -f "-k1.%0.0f" $(<"$f" wc -L) -1 1) "$f" | head -n1

yang sama dengan:

f=file.in; 
l=$(<"$f" wc -L)
k=$(seq -f "-k1.%0.0f" $l -1 1) 
sort -st'\0' $k "$f" | head -n1
Peter.O
sumber
2

Dengan asumsi garis kosong tidak dianggap sebagai garis terpendek dan garis kosong itu mungkin ada, AWK murni berikut ini akan berfungsi:

awk '
    {
        len   = length;
        a[$0] = len
    }
    !len { next }
    !min { min = len }
    len < min { min = len }
    END {
        for (i in a)
            if (min == a[i])
                print i
    }
' infile.txt
snth
sumber
2

Bagaimana dengan menggunakan sortir?

awk '{ print length($0) "\t" $0 }' input.txt | sort -n | head -n 1 | cut -f2-
Gaurav
sumber
1

Dengan GNU awk

gawk '
    {
         a[length]=$0
    };
    END
    {
        PROCINFO["sorted_in"]="@ind_num_asc";
        for (i in a)
        {
            print a[i]; 
            exit
        }
    }
    ' file
  • Baca setiap baris menjadi array yang diindeks oleh panjang garis.

  • Setel PROCINFO["sorted_in"]untuk @ind_num_ascmemaksa pemindaian array agar dipesan oleh indeks array, diurutkan secara numerik

  • Pengaturan PROCINFOdengan cara di atas memaksa garis dengan panjang terkecil untuk diambil pertama kali dalam lintasan array. Jadi cetak elemen pertama dari array dan keluar

Ini memiliki kelemahan karena nlognbeberapa saat dari beberapa pendekatan lain ntepat waktu

iruvar
sumber
1

Metode alat shell tingkat menengah, tanpa sedatau awk:

f=inputfile
head -n $(xargs -d '\n' -L 1 -I % sh -c 'exec echo "%" | wc -c' < $f | 
          cat -n | sort -n -k 2 | head -1 | cut -f 1)  $f | tail -1
agc
sumber
Akan menyenangkan untuk tidak memerlukan $fvariabel; Saya punya gagasan yang mungkin bisa menggunakan teeentah bagaimana ...
AGC