Mengapa * tidak * mem-parsing `ls` (dan apa yang harus dilakukan)?

204

Saya secara konsisten melihat jawaban yang mengutip tautan ini yang menyatakan dengan pasti, "Jangan parsing ls!" Ini menggangguku karena beberapa alasan:

  1. Tampaknya informasi dalam tautan tersebut telah diterima secara grosir dengan sedikit pertanyaan, meskipun saya dapat memilih setidaknya beberapa kesalahan dalam membaca santai.

  2. Tampaknya juga masalah yang dinyatakan dalam tautan tersebut tidak memicu keinginan untuk menemukan solusi.

Dari paragraf pertama:

... ketika Anda meminta [ls]daftar file, ada masalah besar: Unix memungkinkan hampir semua karakter dalam nama file, termasuk spasi, baris baru, koma, simbol pipa, dan hampir semua hal lain yang pernah Anda coba gunakan sebagai pembatas kecuali NUL. ... lsmemisahkan nama file dengan baris baru. Ini bagus sampai Anda memiliki file dengan baris baru dalam namanya. Dan karena saya tidak tahu implementasi apa pun lsyang memungkinkan Anda untuk mengakhiri nama file dengan karakter NUL alih-alih baris baru, ini membuat kami tidak dapat memperoleh daftar nama file dengan aman ls.

Nyebelin, kan? Bagaimana pernah kita dapat menangani baris baru dihentikan dataset terdaftar untuk data yang mungkin berisi baris baru? Nah, jika orang-orang yang menjawab pertanyaan di situs web ini tidak melakukan hal semacam ini setiap hari, saya mungkin berpikir kami berada dalam masalah.

Kenyataannya adalah, sebagian besar lsimplementasi sebenarnya menyediakan api yang sangat sederhana untuk mem-parsing output mereka dan kita semua sudah melakukannya tanpa menyadarinya. Anda tidak hanya dapat mengakhiri nama file dengan null, Anda juga dapat memulai dengan nama null atau dengan string arbitrer lainnya yang mungkin Anda inginkan. Terlebih lagi, Anda dapat menetapkan string sewenang-wenang ini per jenis file . Tolong pertimbangkan:

LS_COLORS='lc=\0:rc=:ec=\0\0\0:fi=:di=:' ls -l --color=always | cat -A
total 4$
drwxr-xr-x 1 mikeserv mikeserv 0 Jul 10 01:05 ^@^@^@^@dir^@^@^@/$
-rw-r--r-- 1 mikeserv mikeserv 4 Jul 10 02:18 ^@file1^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 01:08 ^@file2^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 02:27 ^@new$
line$
file^@^@^@$
^@

Lihat ini untuk lebih lanjut.

Sekarang bagian selanjutnya dari artikel ini yang benar-benar membuat saya:

$ ls -l
total 8
-rw-r-----  1 lhunath  lhunath  19 Mar 27 10:47 a
-rw-r-----  1 lhunath  lhunath   0 Mar 27 10:47 a?newline
-rw-r-----  1 lhunath  lhunath   0 Mar 27 10:47 a space

Masalahnya adalah bahwa dari output ls, Anda atau komputer tidak dapat menentukan bagian mana yang merupakan nama file. Apakah itu setiap kata? Tidak. Apakah itu setiap baris? Tidak. Tidak ada jawaban yang benar untuk pertanyaan ini selain: Anda tidak tahu.

Perhatikan juga bagaimana lskadang-kadang data data file Anda rusak (dalam kasus kami, itu mengubah \nkarakter di antara kata "a" dan "baris baru" menjadi tanda tanya? ...

...

Jika Anda hanya ingin mengulang semua file dalam direktori saat ini, gunakan forloop dan glob:

for f in *; do
    [[ -e $f ]] || continue
    ...
done

Penulis menyebutnya mengacaukan nama file ketika lsmengembalikan daftar nama file yang mengandung shell shell dan kemudian merekomendasikan menggunakan shell glob untuk mengambil daftar file!

Pertimbangkan yang berikut ini:

printf 'touch ./"%b"\n' "file\nname" "f i l e n a m e" |
    . /dev/stdin
ls -1q

f i l e n a m e  
file?name

IFS="
" ; printf "'%s'\n" $(ls -1q)

'f i l e n a m e'
'file
name'

POSIX mendefinisikan yang -1dan -q lsoperan sehingga:

-q- Paksa setiap instance karakter nama file yang tidak dapat dicetak dan <tab>s ditulis sebagai karakter tanda tanya ( '?'). Implementasi dapat menyediakan opsi ini secara default jika outputnya ke perangkat terminal.

-1- (Digit angka satu.) Memaksa output menjadi satu entri per baris.

Globbing bukan tanpa masalah sendiri - ?cocok dengan karakter apa pun sehingga beberapa ?hasil pencocokan dalam daftar akan cocok dengan file yang sama beberapa kali. Itu mudah ditangani.

Meskipun bagaimana melakukan hal ini bukan itu intinya - tidak banyak yang harus dilakukan dan ditunjukkan di bawah ini - saya tertarik mengapa tidak . Saat saya mempertimbangkannya, jawaban terbaik untuk pertanyaan itu telah diterima. Saya sarankan Anda mencoba untuk lebih sering fokus memberi tahu orang lain apa yang bisa mereka lakukan daripada apa yang tidak bisa mereka lakukan . Sepertinya Anda jauh lebih kecil kemungkinannya untuk terbukti salah.

Tetapi mengapa bahkan mencoba? Memang, motivasi utama saya adalah bahwa orang lain terus mengatakan kepada saya bahwa saya tidak bisa. Saya tahu betul bahwa lskeluarannya teratur dan dapat diprediksi seperti yang Anda harapkan selama Anda tahu apa yang harus dicari. Informasi yang salah menggangguku lebih daripada melakukan banyak hal.

Yang benar adalah, meskipun, dengan perkecualian terkemuka untuk jawaban Patrick dan Wumpus Q. Wumbley (terlepas dari pegangan luar biasa yang terakhir) , saya menganggap sebagian besar informasi dalam jawaban di sini sebagian besar benar - sebuah bola shell lebih mudah digunakan. dan umumnya lebih efektif ketika mencari direktori saat ini daripada parsing ls. Namun, mereka tidak, setidaknya menurut saya, cukup alasan untuk membenarkan menyebarkan informasi salah yang dikutip dalam artikel di atas dan mereka juga tidak bisa dibenarkan untuk " tidak pernah parse ls. "

Harap dicatat bahwa hasil yang tidak konsisten Patrick jawaban ini sebagian besar hasil dari dia menggunakan zshitu bash. zsh- secara default - tidak $(perintah -kata menggantikan )hasil dengan cara yang portabel. Jadi ketika dia bertanya kemana sisa file pergi? jawaban atas pertanyaan itu adalah kulitmu memakannya. Inilah sebabnya mengapa Anda perlu mengatur SH_WORD_SPLITvariabel saat menggunakan zshdan menangani kode shell portabel. Saya menganggap kegagalannya untuk mencatat ini dalam jawabannya sangat menyesatkan.

Jawaban Wumpus tidak cocok untuk saya - dalam konteks daftar ?karakternya adalah sebuah shell glob. Saya tidak tahu bagaimana lagi mengatakan itu.

Untuk menangani kasus multi hasil, Anda harus membatasi kerakusan glob. Berikut ini hanya akan membuat basis uji nama file yang mengerikan dan menampilkannya untuk Anda:

{ printf %b $(printf \\%04o `seq 0 127`) |
sed "/[^[-b]*/s///g
        s/\(.\)\(.\)/touch '?\v\2' '\1\t\2' '\1\n\2'\n/g" |
. /dev/stdin

echo '`ls` ?QUOTED `-m` COMMA,SEP'
ls -qm
echo ; echo 'NOW LITERAL - COMMA,SEP'
ls -m | cat
( set -- * ; printf "\nFILE COUNT: %s\n" $# )
}

KELUARAN

`ls` ?QUOTED `-m` COMMA,SEP
??\, ??^, ??`, ??b, [?\, [?\, ]?^, ]?^, _?`, _?`, a?b, a?b

NOW LITERAL - COMMA,SEP
?
 \, ?
     ^, ?
         `, ?
             b, [       \, [
\, ]    ^, ]
^, _    `, _
`, a    b, a
b

FILE COUNT: 12

Sekarang aku akan aman setiap karakter yang bukan /slash, -dash, :colon, atau alpha-numerik karakter dalam segumpal shell kemudian sort -udaftar untuk hasil yang unik. Ini aman karena lstelah menyelamatkan karakter yang tidak dapat dicetak untuk kita. Menonton:

for f in $(
        ls -1q |
        sed 's|[^-:/[:alnum:]]|[!-\\:[:alnum:]]|g' |
        sort -u | {
                echo 'PRE-GLOB:' >&2
                tee /dev/fd/2
                printf '\nPOST-GLOB:\n' >&2
        }
) ; do
        printf "FILE #$((i=i+1)): '%s'\n" "$f"
done

KELUARAN:

PRE-GLOB:
[!-\:[:alnum:]][!-\:[:alnum:]][!-\:[:alnum:]]
[!-\:[:alnum:]][!-\:[:alnum:]]b
a[!-\:[:alnum:]]b

POST-GLOB:
FILE #1: '?
           \'
FILE #2: '?
           ^'
FILE #3: '?
           `'
FILE #4: '[     \'
FILE #5: '[
\'
FILE #6: ']     ^'
FILE #7: ']
^'
FILE #8: '_     `'
FILE #9: '_
`'
FILE #10: '?
            b'
FILE #11: 'a    b'
FILE #12: 'a
b'

Di bawah ini saya mendekati masalah lagi tetapi saya menggunakan metodologi yang berbeda. Ingat bahwa - selain \0nol - /karakter ASCII adalah satu-satunya byte yang dilarang dalam pathname. Saya kesampingkan gumpalan di sini dan sebagai gantinya menggabungkan opsi POSIX yang ditentukan -duntuk lsdan juga -exec $cmd {} +konstruksi POSIX yang ditentukan untuk find. Karena findhanya akan secara alami memancarkan satu /secara berurutan, yang berikut ini dengan mudah mendapatkan daftar file yang rekursif dan dibatasi dengan andal termasuk semua informasi gigi untuk setiap entri. Bayangkan saja apa yang mungkin Anda lakukan dengan sesuatu seperti ini:

#v#note: to do this fully portably substitute an actual newline \#v#
#v#for 'n' for the first sed invocation#v#
cd ..
find ././ -exec ls -1ldin {} + |
sed -e '\| *\./\./|{s||\n.///|;i///' -e \} |
sed 'N;s|\(\n\)///|///\1|;$s|$|///|;P;D'

###OUTPUT

152398 drwxr-xr-x 1 1000 1000        72 Jun 24 14:49
.///testls///

152399 -rw-r--r-- 1 1000 1000         0 Jun 24 14:49
.///testls/?
            \///

152402 -rw-r--r-- 1 1000 1000         0 Jun 24 14:49
.///testls/?
            ^///

152405 -rw-r--r-- 1 1000 1000         0 Jun 24 14:49
.///testls/?
        `///
...

ls -i bisa sangat berguna - terutama ketika keunikan hasil dipertanyakan.

ls -1iq | 
sed '/ .*/s///;s/^/-inum /;$!s/$/ -o /' | 
tr -d '\n' | 
xargs find

Ini hanyalah cara yang paling portabel yang dapat saya pikirkan. Dengan GNU lsyang bisa Anda lakukan:

ls --quoting-style=WORD

Dan yang terakhir, inilah metode parsingls yang lebih sederhana yang sering saya gunakan ketika membutuhkan nomor inode:

ls -1iq | grep -o '^ *[0-9]*'

Itu hanya mengembalikan nomor inode - yang merupakan opsi POSIX berguna lainnya.

mikeserv
sumber
12
@ mikeserv Ok saya lakukan. Shell glob adalah 2,48 kali lebih cepat. time bash -c 'for i in {1..1000}; do ls -R &>/dev/null; done'= 3.18s vs time bash -c 'for i in {1..1000}; do echo **/* >/dev/null; done'= 1.28s
Patrick
28
Sehubungan dengan pembaruan terbaru Anda, harap berhenti mengandalkan output visual karena menentukan bahwa kode Anda berfungsi. Lewatkan output Anda ke program yang sebenarnya dan minta program mencoba dan melakukan operasi pada file. Inilah sebabnya saya menggunakan statjawaban saya, karena sebenarnya memeriksa bahwa setiap file ada. Bit Anda di bagian bawah dengan sedhal itu tidak berfungsi.
Patrick
57
Anda tidak bisa serius. Bagaimana bisa melompati semua simpai yang dijelaskan oleh pertanyaan Anda menjadi lebih mudah atau lebih sederhana atau dengan cara apa pun lebih baik daripada tidak menguraikan lspada awalnya? Apa yang Anda gambarkan sangat sulit. Saya harus mendekonstruksi untuk memahami semua itu dan saya pengguna yang relatif kompeten. Anda tidak mungkin mengharapkan rata-rata Joe Anda untuk dapat menangani sesuatu seperti ini.
terdon
46
-1 untuk menggunakan pertanyaan untuk memilih argumen. Semua alasan parsing lsoutput salah tercakup dengan baik di tautan asli (dan di banyak tempat lain). Pertanyaan ini akan masuk akal jika OP meminta bantuan untuk memahaminya, tetapi sebaliknya OP hanya mencoba membuktikan bahwa penggunaannya yang salah itu tidak masalah.
R ..
14
@ mikeserv Bukan hanya itu parsing ls is bad. Melakukan for something in $(command)dan mengandalkan pemisahan kata untuk mendapatkan hasil yang akurat adalah buruk bagi sebagian besar command'syang tidak memiliki output sederhana.
BroSlow

Jawaban:

184

Saya sama sekali tidak yakin akan hal ini, tetapi anggaplah demi argumen bahwa Anda dapat , jika Anda siap untuk melakukan upaya yang cukup, parsing output lsandal, bahkan dalam menghadapi "musuh" - seseorang yang tahu kode yang Anda tulis dan sengaja memilih nama file yang dirancang untuk memecahkannya.

Bahkan jika Anda bisa melakukan itu, itu masih merupakan ide yang buruk .

Shell Bourne bukan bahasa yang baik. Seharusnya tidak digunakan untuk hal yang rumit, kecuali portabilitas ekstrim lebih penting daripada faktor lain (misalnya autoconf).

Saya mengklaim bahwa jika Anda dihadapkan dengan masalah di mana parsing output lssepertinya jalan perlawanan paling sedikit untuk skrip shell, itu indikasi kuat bahwa apa pun yang Anda lakukan terlalu rumit untuk shell dan Anda harus menulis ulang seluruh hal dalam Perl atau Python. Inilah program terakhir Anda dengan Python:

import os, sys
for subdir, dirs, files in os.walk("."):
    for f in dirs + files:
      ino = os.lstat(os.path.join(subdir, f)).st_ino
      sys.stdout.write("%d %s %s\n" % (ino, subdir, f))

Ini tidak memiliki masalah apa pun dengan karakter yang tidak biasa dalam nama file - outputnya ambigu dengan cara yang sama dengan output lsyang ambigu, tetapi itu tidak masalah dalam program "nyata" (sebagai lawan dari demo seperti ini), yang akan gunakan hasil os.path.join(subdir, f)langsung.

Sama pentingnya, dan sangat kontras dengan apa yang Anda tulis, masih masuk akal enam bulan dari sekarang, dan akan mudah untuk dimodifikasi ketika Anda membutuhkannya untuk melakukan sesuatu yang sedikit berbeda. Sebagai ilustrasi, misalkan Anda menemukan kebutuhan untuk mengecualikan dotfiles dan cadangan editor, dan untuk memproses semuanya dalam urutan abjad dengan nama kecil:

import os, sys
filelist = []
for subdir, dirs, files in os.walk("."):
    for f in dirs + files:
        if f[0] == '.' or f[-1] == '~': continue
        lstat = os.lstat(os.path.join(subdir, f))
        filelist.append((f, subdir, lstat.st_ino))

filelist.sort(key = lambda x: x[0])
for f, subdir, ino in filelist: 
   sys.stdout.write("%d %s %s\n" % (ino, subdir, f))
zwol
sumber
5
Ini bagus. Apakah itu for in | for inberbicara tentang rekursi? Saya tidak yakin. Bahkan jika itu tidak lebih dari satu, bukan? Inilah satu-satunya jawaban yang masuk akal bagi saya sejauh ini.
mikeserv
10
Tidak ada rekursi, hanya bersarang for-loops os.walkmelakukan beberapa pengangkatan serius di belakang layar, tetapi Anda tidak perlu khawatir tentang hal itu lebih daripada Anda harus khawatir tentang bagaimana lsatau findbekerja secara internal.
zwol
6
Secara teknis, os.walkmengembalikan objek generator . Generator adalah daftar malas versi Python. Setiap kali luar untuk loop berulang, generator dipanggil dan "menghasilkan" isi subdirektori lain. Fungsionalitas yang setara dalam Perl adalah File::Find, jika itu membantu.
zwol
6
Anda harus menyadari bahwa saya 100% setuju dengan dokumen yang Anda kritik dan dengan jawaban Patrick dan Terdon. Jawaban saya dimaksudkan untuk memberikan alasan tambahan independen untuk menghindari parsing lsoutput.
zwol
19
Ini sangat menyesatkan. Shell bukan bahasa pemrograman yang baik, tetapi hanya karena itu bukan bahasa pemrograman. Ini bahasa scripting. Dan itu bahasa scripting yang bagus.
Miles Rout
178

Tautan itu banyak dirujuk karena informasinya benar-benar akurat, dan sudah lama ada di sana.


lsmengganti karakter yang tidak dapat dicetak dengan karakter glob ya, tetapi karakter tersebut tidak ada dalam nama file yang sebenarnya. Mengapa ini penting? 2 alasan:

  1. Jika Anda meneruskan nama file itu ke sebuah program, nama file itu sebenarnya tidak ada. Itu harus memperluas glob untuk mendapatkan nama file asli.
  2. File glob mungkin cocok dengan lebih dari satu file.

Sebagai contoh:

$ touch a$'\t'b
$ touch a$'\n'b
$ ls -1
a?b
a?b

Perhatikan bagaimana kita memiliki 2 file yang terlihat persis sama. Bagaimana Anda akan membedakan mereka jika keduanya diwakili a?b?


Penulis menyebutnya mengacaukan nama file ketika ia mengembalikan daftar nama file yang mengandung shell shell dan kemudian merekomendasikan menggunakan shell glob untuk mengambil daftar file!

Ada perbedaan disini. Ketika Anda mendapatkan bola kembali, seperti yang ditunjukkan, bola itu mungkin cocok dengan lebih dari satu file. Namun ketika Anda mengulangi melalui hasil yang cocok dengan bola, Anda mendapatkan kembali file yang tepat, bukan bola.

Sebagai contoh:

$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6109 62                                  a.b
0000000: 610a 62                                  a.b

Perhatikan bagaimana xxdoutput menunjukkan yang $fileberisi karakter mentah \tdan \n, bukan ?.

Jika Anda menggunakan ls, Anda mendapatkan ini sebagai gantinya:

for file in $(ls -1q); do printf '%s' "$file" | xxd; done
0000000: 613f 62                                  a?b
0000000: 613f 62                                  a?b

"Lagi pula aku akan beralih, mengapa tidak menggunakan ls?"

Contoh yang Anda berikan tidak benar-benar berfungsi. Sepertinya itu berfungsi, tetapi tidak.

Saya mengacu pada ini:

 for f in $(ls -1q | tr " " "?") ; do [ -f "$f" ] && echo "./$f" ; done

Saya telah membuat direktori dengan banyak nama file:

$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6120 62                                  a b
0000000: 6120 2062                                a  b
0000000: 61e2 8082 62                             a...b
0000000: 61e2 8083 62                             a...b
0000000: 6109 62                                  a.b
0000000: 610a 62                                  a.b

Ketika saya menjalankan kode Anda, saya mendapatkan ini:

$ for f in $(ls -1q | tr " " "?") ; do [ -f "$f" ] && echo "./$f" ; done
./ab
./ab

Ke mana sisa file pergi?

Mari kita coba ini sebagai gantinya:

$ for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f"; done
stat: cannot stat ‘./a?b’: No such file or directory
stat: cannot stat ‘./a??b’: No such file or directory
./ab
./ab
stat: cannot stat ‘./a?b’: No such file or directory
stat: cannot stat ‘./a?b’: No such file or directory

Sekarang mari kita gunakan gumpalan yang sebenarnya:

$ for f in *; do stat --format='%n' "./$f"; done
./a b
./a  b
./ab
./ab
./a b
./a
b

Dengan bash

Contoh di atas adalah dengan shell normal saya, zsh. Ketika saya mengulangi prosedur dengan bash, saya mendapatkan serangkaian hasil yang sama sekali berbeda dengan contoh Anda:

Kumpulan file yang sama:

$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6120 62                                  a b
0000000: 6120 2062                                a  b
0000000: 61e2 8082 62                             a...b
0000000: 61e2 8083 62                             a...b
0000000: 6109 62                                  a.b
0000000: 610a 62                                  a.b

Hasil yang sangat berbeda dengan kode Anda:

for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f"; done
./a b
./ab
./ab
./a b
./a
b
./a  b
./ab
./ab
./a b
./ab
./ab
./a b
./a
b
./a b
./ab
./ab
./a b
./a
b

Dengan glob shell, ia bekerja dengan sangat baik:

$ for f in *; do stat --format='%n' "./$f"; done
./a b
./a  b
./ab
./ab
./a b
./a
b

Alasan bash berperilaku seperti ini kembali ke salah satu poin yang saya buat di awal jawaban: "File glob mungkin cocok dengan lebih dari satu file".

lsmengembalikan glob yang sama ( a?b) untuk beberapa file, jadi setiap kali kami memperluas glob ini, kami mendapatkan setiap file yang cocok dengannya.


Cara membuat ulang daftar file yang saya gunakan:

touch 'a b' 'a  b' a$'\xe2\x80\x82'b a$'\xe2\x80\x83'b a$'\t'b a$'\n'b

Kode hex adalah karakter UTF-8 NBSP.

Patrick
sumber
5
@ mikeserv sebenarnya solusinya tidak mengembalikan bola. Saya baru saja memperbarui jawaban saya untuk menjelaskan hal itu.
Patrick
18
"Bukan sisanya"? Itu perilaku yang tidak konsisten, dan hasil yang tidak terduga, bagaimana itu bukan alasan?
Patrick
11
@ mikeserv Apakah Anda tidak melihat komentar saya pada pertanyaan Anda? Shell globbing 2.5 kali lebih cepat dari ls. Saya juga meminta Anda menguji kode Anda karena tidak berfungsi. Apa yang harus dilakukan zsh dengan semua ini?
Patrick
27
@ mikeserv Tidak, semuanya masih berlaku bahkan untuk bash. Meskipun saya sudah selesai dengan pertanyaan ini karena Anda tidak mendengarkan apa yang saya katakan.
Patrick
7
Anda tahu, saya pikir saya akan mengangkat jawaban ini dan mengklarifikasi jawaban saya bahwa saya setuju dengan semua yang dikatakannya. ;-)
zwol
54

Mari kita coba dan sederhanakan sedikit:

$ touch a$'\n'b a$'\t'b 'a b'
$ ls
a b  a?b  a?b
$ IFS="
"
$ set -- $(ls -1q | uniq)
$ echo "Total files in shell array: $#"
Total files in shell array: 4

Lihat? Itu sudah salah di sana. Ada 3 file tetapi bash melaporkan 4. Ini karena setsedang diberikan gumpalan yang dihasilkan oleh lsdiperluas oleh shell sebelum diteruskan ke set. Itu sebabnya Anda mendapatkan:

$ for x ; do
>     printf 'File #%d: %s\n' $((i=$i+1)) "$x"
> done
File #1: a b
File #2: a b
File #3: a    b
File #4: a
b

Atau, jika Anda lebih suka:

$ printf ./%s\\0 "$@" |
> od -A n -c -w1 |
> sed -n '/ \{1,3\}/s///;H
> /\\0/{g;s///;s/\n//gp;s/.*//;h}'
./a b
./a b
./a\tb
./a\nb

Di atas dijalankan bash 4.2.45.

terdon
sumber
2
Saya membatalkan ini. Senang melihat kode Anda sendiri menggigit Anda. Tetapi hanya karena saya salah, bukan berarti itu tidak dapat dilakukan dengan benar. Saya menunjukkan kepada Anda cara yang sangat sederhana untuk melakukannya pagi ini ls -1qRi | grep -o '^ *[0-9]*'- itu adalah lshasil parsing , man, dan ini adalah cara tercepat dan terbaik yang saya tahu untuk mendapatkan daftar nomor inode.
mikeserv
38
@ mikeserv: Itu bisa dilakukan dengan benar, jika Anda punya waktu dan kesabaran. Tapi faktanya, itu secara inheren rawan kesalahan. Anda sendiri salah. sambil berdebat tentang manfaatnya! Itu adalah serangan besar terhadapnya, jika bahkan satu orang yang berjuang untuk itu gagal melakukannya dengan benar. Dan kemungkinan besar, Anda mungkin akan menghabiskan lebih banyak waktu untuk melakukan kesalahan sebelum melakukannya dengan benar. Saya tidak tahu tentang Anda, tetapi kebanyakan orang lebih baik melakukan dengan waktu mereka daripada bermain-main untuk usia dengan baris kode yang sama.
cao
@ cHao - saya tidak memperdebatkan manfaatnya - saya memprotes propagandanya.
mikeserv
16
@ mikeserv: Argumen yang menentangnya beralasan dan layak. Bahkan Anda telah menunjukkan bahwa itu benar.
cao
1
@ cHao - saya tidak setuju. Ada garis yang tidak terlalu halus antara mantra dan kebijaksanaan.
mikeserv
50

Output dari ls -qbukan glob sama sekali. Dulu ?berarti "Ada karakter di sini yang tidak dapat ditampilkan secara langsung". Gumpalan digunakan ?berarti "Setiap karakter diizinkan di sini".

Gumpalan memiliki karakter khusus lainnya ( *dan []setidaknya, dan di dalam []pasangan ada lebih banyak). Tak satu pun dari mereka yang lolos ls -q.

$ touch x '[x]'
$ ls -1q
[x]
x

Jika Anda memperlakukan ls -1qoutput ada satu set gumpalan dan mengembangkannya, Anda tidak hanya akan mendapatkan xdua kali, Anda akan kehilangan [x]sepenuhnya. Sebagai gumpalan, itu tidak cocok dengan dirinya sendiri sebagai string.

ls -q dimaksudkan untuk menyelamatkan mata dan / atau terminal Anda dari karakter gila, bukan untuk menghasilkan sesuatu yang dapat Anda umpan balik ke shell.


sumber
42

Jawabannya sederhana: Kasus khusus lsAnda harus menangani lebih besar daripada manfaat apa pun yang mungkin. Kasing khusus ini dapat dihindari jika Anda tidak menguraikan lsoutput.

Mantra di sini tidak pernah mempercayai sistem file pengguna (setara dengan tidak pernah mempercayai input pengguna ). Jika ada metode yang akan selalu berhasil, dengan kepastian 100%, itu harus menjadi metode yang Anda sukai meskipun lsmelakukan hal yang sama tetapi dengan lebih sedikit kepastian. Saya tidak akan membahas detail teknis karena hal itu dibahas oleh terdon dan Patrick secara luas. Saya tahu bahwa karena risiko menggunakan lsdalam transaksi penting (dan mungkin mahal) di mana pekerjaan / prestise saya ada di telepon, saya akan lebih memilih solusi yang tidak memiliki tingkat ketidakpastian jika dapat dihindari.

Saya tahu beberapa orang lebih suka risiko daripada kepastian , tetapi saya sudah mengajukan laporan bug .

Braiam
sumber
33

Alasan orang mengatakan tidak pernah melakukan sesuatu belum tentu karena itu benar-benar positif tidak dapat dilakukan dengan benar. Kita mungkin dapat melakukannya, tetapi mungkin lebih rumit, kurang efisien baik dari segi ruang maupun waktu. Sebagai contoh itu akan baik-baik saja untuk mengatakan "Jangan pernah membangun backend e-commerce besar di perakitan x86".

Jadi sekarang untuk masalah yang ada: Seperti yang telah Anda tunjukkan, Anda dapat membuat solusi yang mem-parsing ls dan memberikan hasil yang tepat - jadi kebenaran bukanlah masalah.

Apakah ini lebih rumit? Ya, tapi kita bisa menyembunyikannya di balik fungsi pembantu.

Jadi sekarang untuk efisiensi:

Efisiensi ruang: Solusi Anda mengandalkan uniquntuk memfilter duplikat, akibatnya kami tidak dapat menghasilkan hasil dengan malas. Jadi O(1)vs O(n)atau keduanya miliki O(n).

Efisiensi waktu: Kasus terbaik uniqmenggunakan pendekatan hashmap sehingga kami masih memiliki O(n)algoritma dalam jumlah elemen yang dibeli , mungkin meskipun itu O(n log n).

Sekarang masalah sebenarnya: Meskipun algoritma Anda masih tidak terlihat terlalu buruk, saya benar-benar berhati-hati untuk menggunakan elemen yang dibeli dan bukan elemen untuk n. Karena itu memang membuat perbedaan besar. Katakanlah Anda memiliki file \n\nyang akan menghasilkan glob untuk ??jadi cocokkan setiap 2 karakter file dalam daftar. Lucunya jika Anda memiliki file lain \n\ryang juga akan menghasilkan ??dan juga mengembalikan semua 2 file karakter .. lihat di mana ini? Eksponensial daripada perilaku linier tentu memenuhi syarat sebagai "perilaku runtime yang lebih buruk" .. itu adalah perbedaan antara algoritma praktis dan yang Anda tulis makalah dalam jurnal CS teoritis tentang.

Semua orang suka contoh, bukan? Kita mulai. Buat folder yang disebut "test" dan gunakan skrip python ini di direktori yang sama di mana folder tersebut berada.

#!/usr/bin/env python3
import itertools
dir = "test/"
filename_length = 3
options = "\a\b\t\n\v\f\r"

for filename in itertools.product(options, repeat=filename_length):
        open(dir + ''.join(filename), "a").close()

Satu-satunya hal yang dilakukan adalah menghasilkan semua produk dengan panjang 3 untuk 7 karakter. Matematika sekolah menengah memberi tahu kita bahwa seharusnya ada 343 file. Nah itu seharusnya sangat cepat untuk dicetak, jadi mari kita lihat:

time for f in *; do stat --format='%n' "./$f" >/dev/null; done
real    0m0.508s
user    0m0.051s
sys 0m0.480s

Sekarang mari kita coba solusi pertama Anda, karena saya benar-benar tidak bisa mendapatkan ini

eval set -- $(ls -1qrR ././ | tr ' ' '?' |
sed -e '\|^\(\.\{,1\}\)/\.\(/.*\):|{' -e \
        's//\1\2/;\|/$|!s|.*|&/|;h;s/.*//;b}' -e \
        '/..*/!d;G;s/\(.*\)\n\(.*\)/\2\1/' -e \
        "s/'/'\\\''/g;s/.*/'&'/;s/?/'[\"?\$IFS\"]'/g" |
uniq)

hal di sini untuk bekerja di Linux mint 16 (yang saya pikir berbicara banyak untuk kegunaan metode ini).

Bagaimanapun karena di atas cukup banyak hanya menyaring hasil setelah mendapatkannya, solusi sebelumnya harus setidaknya secepat yang kemudian (tidak ada trik inode dalam satu itu - tetapi mereka tidak dapat diandalkan sehingga Anda akan memberikan kebenaran).

Jadi sekarang berapa lama

time for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f" >/dev/null; done

mengambil? Ya saya benar-benar tidak tahu, perlu waktu untuk memeriksa 343 ^ 343 nama file - Saya akan memberi tahu Anda setelah kematian panas alam semesta.

Voo
sumber
6
Tentu saja, seperti yang disebutkan dalam komentar di bawah jawaban lain , pernyataan "... Anda telah menunjukkan Anda dapat membuat solusi yang mem-parsing dan memberikan hasil yang tepat ..." sebenarnya tidak benar.
Wildcard
26

Niat Lain OP Ditujukan

pengantar dan alasan jawaban asli diperbarui pada 2015-05-18

mikeserv (OP) menyatakan dalam pembaruan terakhir untuk pertanyaannya: "Saya menganggapnya memalukan bahwa saya pertama kali menanyakan pertanyaan ini untuk menunjukkan sumber informasi yang salah, dan, sayangnya, jawaban yang paling banyak dipilih di sini sebagian besar menyesatkan. "

Baiklah, oke; Saya merasa agak memalukan karena saya menghabiskan banyak waktu untuk mencari tahu bagaimana menjelaskan makna saya hanya untuk menemukan itu ketika saya membaca kembali pertanyaannya. Pertanyaan ini akhirnya "[menghasilkan] diskusi daripada jawaban" dan berakhir dengan berat ~ 18K teks (untuk pertanyaan saja, hanya untuk menjadi jelas) yang akan lama bahkan untuk posting blog.

Tetapi StackExchange bukan kotak sabun Anda, dan itu bukan blog Anda. Namun, pada dasarnya, Anda telah menggunakannya sebagai setidaknya sedikit dari keduanya. Orang-orang pada akhirnya menghabiskan banyak waktu untuk menjawab "To-Point-Out" Anda alih-alih menjawab pertanyaan orang yang sebenarnya. Pada titik ini saya akan menandai pertanyaan itu sebagai tidak cocok untuk format kita, mengingat bahwa OP telah menyatakan secara eksplisit bahwa itu bahkan tidak dimaksudkan untuk menjadi pertanyaan sama sekali.

Pada titik ini saya tidak yakin apakah jawaban saya to the point, atau tidak; mungkin tidak, tapi itu diarahkan pada beberapa pertanyaan Anda, dan mungkin itu bisa menjadi jawaban yang berguna untuk orang lain; pemula mengambil hati, beberapa dari mereka "tidak" berubah menjadi "lakukan kadang-kadang" setelah Anda lebih berpengalaman. :)

Sebagai aturan umum...

tolong maafkan tepi kasar yang tersisa; Saya telah menghabiskan terlalu banyak waktu untuk ini ... daripada mengutip OP secara langsung (seperti yang dimaksudkan semula), saya akan mencoba merangkum dan memparafrasekan.

[sebagian besar dikerjakan ulang dari jawaban asli saya]
setelah dipertimbangkan, saya percaya bahwa saya salah membaca penekanan yang diberikan OP pada pertanyaan yang saya jawab; Namun, poin ditujukan yang dibesarkan, dan saya telah meninggalkan jawaban utuh karena saya percaya mereka untuk menjadi to-the-point dan untuk mengatasi masalah yang saya sudah terlihat dibesarkan dalam konteks lain juga mengenai saran untuk pemula.

Posting asli bertanya, dalam beberapa cara, mengapa berbagai artikel memberi saran seperti «Jangan parse lsoutput» atau «Anda tidak boleh parse lsoutput», dan sebagainya.

Resolusi yang saya sarankan untuk masalah ini adalah bahwa contoh pernyataan semacam ini hanyalah contoh dari ungkapan, yang diutarakan dengan cara yang sedikit berbeda, di mana kuantifier absolut dipasangkan dengan imperatif [misalnya, «jangan pernah] X», «[Anda harus] selalu Y», «[orang seharusnya] tidak pernah Z»] untuk membentuk pernyataan yang dimaksudkan untuk digunakan sebagai aturan atau pedoman umum, terutama ketika diberikan kepada mereka yang baru dalam suatu subjek, daripada dimaksudkan sebagai kebenaran absolut, jelas bentuk dari pernyataan-pernyataan meskipun.

Ketika Anda mulai mempelajari materi pelajaran baru, dan kecuali Anda memiliki pemahaman yang baik tentang mengapa Anda mungkin perlu melakukan hal lain, adalah ide yang baik untuk hanya mengikuti aturan umum yang diterima tanpa kecuali — kecuali di bawah bimbingan dari seseorang yang lebih berpengalaman. itu sendiri. Dengan meningkatnya keterampilan dan pengalaman, Anda menjadi semakin bisa menentukan kapan dan apakah suatu aturan berlaku dalam situasi tertentu. Setelah Anda mencapai tingkat pengalaman yang signifikan, Anda mungkin akan memahami alasan di balik aturan umum di tempat pertama, dan pada titik itu Anda dapat mulai menggunakan penilaian Anda tentang apakah dan ke tingkat apa alasan di balik aturan berlaku di situasi itu, dan juga apakah ada kekhawatiran utama.

Dan saat itulah seorang ahli, mungkin, mungkin memilih untuk melakukan hal-hal yang melanggar "Peraturan". Tapi itu tidak akan membuat mereka kurang "Aturan".

Dan, jadi, untuk topik yang ada: dalam pandangan saya, hanya karena seorang ahli mungkin dapat melanggar aturan ini tanpa benar-benar menampar, saya tidak melihat cara yang bisa Anda katakan kepada seorang pemula bahwa "kadang-kadang" itu oke untuk mengurai lsoutput, karena: tidak . Atau, setidaknya, tentu saja tidak tepat bagi pemula untuk melakukannya.

Anda selalu meletakkan bidak di tengah; dalam pembukaan sepotong, satu gerakan; kastil pada kesempatan paling awal; ksatria di hadapan uskup; seorang ksatria di tepinya suram; dan selalu pastikan Anda dapat melihat perhitungan Anda sampai akhir! (Aduh, maaf, lelah, itu untuk catur StackExchange.)

Aturan, Dimaksudkan Untuk Patah?

Saat membaca artikel tentang subjek yang ditargetkan pada, atau kemungkinan akan dibaca oleh, pemula, seringkali Anda akan melihat hal-hal seperti ini:

  • "Anda tidak harus pernah melakukan X."
  • "Jangan pernah lakukan Q!"
  • "Jangan lakukan Z."
  • "Orang harus selalu melakukan Y!"
  • "C, apa pun yang terjadi."

Meskipun pernyataan-pernyataan ini tampaknya secara jelas menyatakan aturan yang absolut dan tidak lekang oleh waktu, mereka tidak; alih-alih ini adalah cara untuk menyatakan aturan umum [alias "pedoman", "aturan praktis", "dasar-dasar", dll.) yang setidaknya merupakan salah satu cara yang tepat untuk menyatakannya bagi pemula yang mungkin membaca artikel tersebut. Namun, hanya karena mereka dinyatakan sebagai absolut, aturan tentu tidak mengikat profesional dan ahli [yang kemungkinan orang-orang yang meringkas aturan tersebut di tempat pertama, sebagai cara untuk mencatat dan meneruskan pengetahuan yang diperoleh saat mereka berurusan dengan berulang masalah dalam keahlian khusus mereka.]

Aturan-aturan itu tentu tidak akan mengungkapkan bagaimana seorang ahli akan berurusan dengan masalah yang kompleks atau bernuansa, di mana, katakanlah, aturan-aturan itu saling bertentangan; atau di mana kekhawatiran yang mengarah pada aturan di tempat pertama tidak berlaku. Para ahli tidak takut (atau tidak boleh takut!) Hanya melanggar aturan yang mereka tahu tidak masuk akal dalam situasi tertentu. Para ahli terus-menerus berurusan dengan menyeimbangkan berbagai risiko dan kekhawatiran dalam keahlian mereka, dan harus sering menggunakan penilaian mereka untuk memilih untuk melanggar aturan semacam itu, harus menyeimbangkan berbagai faktor dan tidak dapat hanya mengandalkan tabel aturan yang harus diikuti. Ambillah Gotosebagai contoh: telah terjadi perdebatan yang panjang dan berulang tentang apakah mereka berbahaya. (Ya, jangan pernah menggunakan goto.; D)

Proposisi Modal

Fitur aneh, setidaknya dalam bahasa Inggris, dan saya bayangkan dalam banyak bahasa lain, dari aturan umum, adalah bahwa mereka dinyatakan dalam bentuk yang sama dengan proposisi modal, namun para ahli di bidang bersedia untuk memberikan aturan umum untuk suatu situasi, sambil mengetahui bahwa mereka akan melanggar aturan bila perlu. Jelas, oleh karena itu, pernyataan ini tidak dimaksudkan untuk setara dengan pernyataan yang sama dalam logika modal.

Inilah mengapa saya katakan mereka hanya idiomatis. Daripada benar-benar menjadi situasi "tidak pernah" atau "selalu", aturan-aturan ini biasanya berfungsi untuk menyusun pedoman umum yang cenderung sesuai untuk berbagai situasi, dan bahwa, ketika pemula mengikuti mereka secara membabi buta, cenderung menghasilkan jauh hasil yang lebih baik daripada pemula memilih untuk melawan mereka tanpa alasan yang bagus. Kadang-kadang mereka menyusun aturan hanya mengarah ke hasil di bawah standar daripada kegagalan langsung menyertai pilihan yang salah ketika bertentangan dengan aturan.

Jadi, aturan umum bukanlah proposisi modal absolut yang tampaknya ada di permukaan, tetapi sebaliknya adalah cara singkat memberikan aturan dengan pelat baja standar yang tersirat, seperti berikut ini:

kecuali Anda memiliki kemampuan untuk mengatakan bahwa pedoman ini tidak benar dalam kasus tertentu, dan buktikan kepada diri sendiri bahwa Anda benar, maka $ {ATURAN}

di mana, tentu saja Anda dapat mengganti " lsoutput tidak pernah diuraikan " sebagai pengganti $ {ATURAN}. :)

Oh ya! Apa Tentang Parsing lsoutput?

Nah, jadi, mengingat semua itu ... saya pikir cukup jelas bahwa aturan ini bagus. Pertama-tama, aturan sebenarnya harus dipahami idiomatik, seperti yang dijelaskan di atas ...

Tapi lebih jauh lagi, bukan hanya Anda harus sangat baik dengan skrip shell untuk mengetahui apakah itu dapat rusak, dalam beberapa kasus tertentu. Juga, dibutuhkan keterampilan yang sama banyaknya untuk mengatakan bahwa Anda salah ketika Anda mencoba untuk mematahkannya dalam pengujian! Dan, saya katakan dengan percaya diri bahwa mayoritas yang sangat besar dari pendengar artikel tersebut (memberikan saran seperti «Jangan parsing output dari ls!») Tidak dapat melakukan hal-hal itu , dan mereka yang memiliki keterampilan seperti itu kemungkinan akan menyadari bahwa mereka mengatasinya sendiri dan mengabaikan aturan itu.

Tapi ... lihat saja pertanyaan ini, dan bagaimana bahkan orang yang mungkin memang memiliki keterampilan berpikir itu adalah panggilan yang buruk untuk melakukannya; dan berapa banyak upaya yang dihabiskan oleh penulis pertanyaan untuk sampai pada contoh terbaik saat ini! Saya jamin Anda pada masalah yang sulit, 99% orang di luar sana akan salah, dan dengan hasil yang berpotensi sangat buruk! Bahkan jika metode yang diputuskan ternyata bagus; sampai lside parsing (atau lainnya) diadopsi oleh TI / pengembang secara keseluruhan, tahan banyak pengujian (terutama pengujian waktu) dan, akhirnya, berhasil lulus ke status 'teknik umum', kemungkinan bahwa banyak orang mungkin mencobanya, dan salah ... dengan konsekuensi yang menghancurkan.

Jadi, saya akan mengulangi untuk terakhir kalinya .... bahwa, terutama dalam kasus ini , itulah sebabnya " output tidak pernah diurai ls!" jelas merupakan cara yang tepat untuk mengungkapkannya .

[UPDATE 2014-05-18: penjelasan alasan untuk jawaban (di atas) untuk menanggapi komentar dari OP; penambahan berikut ini sebagai tanggapan atas penambahan OP terhadap pertanyaan dari kemarin]

[PEMBARUAN 2014-11-10: menambahkan tajuk dan konten yang direorganisasi / dire-refaktasikan; dan juga: memformat ulang, menulis ulang, mengklarifikasi, dan um ... "ringkas-ifying" ... saya bermaksud ini hanya menjadi pembersihan, meskipun itu berubah menjadi sedikit pengerjaan ulang. Saya telah meninggalkannya dalam keadaan menyesal, jadi saya terutama mencoba untuk memberikan beberapa pesanan. Saya memang merasa penting untuk membiarkan bagian pertama utuh; jadi hanya ada dua perubahan kecil di sana, redundan 'tapi' dihapus, dan 'itu' ditekankan.]

† Saya awalnya bermaksud ini semata-mata sebagai klarifikasi pada dokumen asli saya; tetapi memutuskan penambahan lainnya pada refleksi

‡ lihat https://unix.stackexchange.com/tour untuk panduan posting

shelleybutterfly
sumber
2
Tidak pernah tidak idiomatis. Ini bukan jawaban untuk apa pun.
mikeserv
1
Hmm. Yah, saya tidak tahu apakah jawaban ini akan memuaskan tetapi saya sama sekali tidak berharap itu menjadi kontroversial . Dan, saya tidak (bermaksud) berpendapat bahwa 'tidak pernah' itu sendiri adalah idiomatis; tapi itu "Never do X!" adalah penggunaan idiomatik . Saya melihat dua kasus umum yang dapat menunjukkan bahwa 'Jangan / jangan parsing ls!' adalah saran yang benar: 1. tunjukkan (untuk kepuasan Anda) bahwa setiap kasus penggunaan di mana seseorang mungkin mengurai lsoutput memiliki solusi lain yang tersedia, lebih unggul dalam beberapa cara, tanpa melakukannya. 2. menunjukkan bahwa, dalam kasus-kasus yang dikutip, pernyataan itu bukan pernyataan literal.
shelleybutterfly
Melihat pertanyaan Anda lagi, saya melihat bahwa Anda pertama kali menyebutkan "jangan ..." daripada "tidak pernah ..." yang baik dalam analisis Anda, jadi saya akan mengklarifikasi tentang hal itu juga. Pada titik ini sudah ada solusi dari tipe pertama, yang tampaknya ditunjukkan / dijelaskan untuk kepuasan Anda, jadi saya tidak akan mempelajari banyak tentang hal itu. Tetapi saya akan mencoba dan mengklarifikasi jawaban saya sedikit: seperti yang saya katakan, saya tidak mencoba untuk menjadi kontroversial (atau konfrontatif!) Tetapi untuk menunjukkan bagaimana pernyataan itu secara umum dimaksudkan.
shelleybutterfly
1
Saya harus membersihkan posting itu. Namun, tidak pernah adalah tidak cara yang tepat untuk frase itu. Agak konyol bahwa orang-orang berpikir mereka memenuhi syarat untuk memberi tahu orang lain tidak pernah atau tidak - katakan saja kepada mereka Anda tidak berpikir itu akan berhasil dan mengapa, tetapi Anda tahu apa yang akan berhasil dan mengapa. lsadalah utilitas komputer - Anda dapat menguraikan output komputer.
mikeserv
1
Yah, saya membalikkan downvote saya karena, paling tidak, Anda benar tentang hal yang lesu. Saya akan mencoba untuk membersihkannya malam ini atau besok. Pikir saya adalah saya akan memindahkan sebagian besar contoh kode ke jawaban saya kira. Tapi itu masih, sejauh menyangkut saya, alasan ketidakakuratan dalam posting blog yang sering dikutip. Saya berharap orang-orang akan berhenti mengutip manual bash sama sekali - setidaknya tidak sampai mereka mengutip spesifikasi POSIX ...
mikeserv
16

Apakah mungkin untuk mengurai output lsdalam kasus-kasus tertentu? Tentu. Ide mengekstraksi daftar nomor inode dari direktori adalah contoh yang bagus - jika Anda tahu bahwa lsdukungan implementasi Anda -q, dan oleh karena itu setiap file akan menghasilkan tepat satu baris output, dan semua yang Anda butuhkan adalah nomor inode, parsing mereka keluar dari ls -Rai1qkeluaran tentu merupakan solusi yang memungkinkan. Tentu saja, jika penulis tidak melihat saran seperti "Jangan pernah menguraikan output ls" sebelumnya, dia mungkin tidak akan memikirkan nama file dengan baris baru di dalamnya, dan mungkin akan meninggalkan 'q' sebagai hasilnya, dan kode akan secara halus rusak dalam kasus tepi - jadi, bahkan dalam kasus di mana lsoutput parsing masuk akal, saran ini masih berguna.

Titik yang lebih luas adalah bahwa, ketika seorang pemula untuk shell scripting mencoba untuk memiliki figur script keluar (misalnya) apa file terbesar di direktori, atau apa yang paling baru-baru ini diubah file dalam direktori, insting pertama adalah untuk mengurai ls's output - dapat dimengerti, karena lsmerupakan salah satu perintah pertama yang dipelajari pemula.

Sayangnya, naluri itu salah, dan pendekatan itu rusak. Bahkan yang lebih disayangkan, ini rusak secara halus - ini akan berfungsi sebagian besar waktu, tetapi gagal dalam kasus tepi yang mungkin bisa dieksploitasi oleh seseorang dengan pengetahuan kode.

Pemula mungkin berpikir ls -s | sort -n | tail -n 1 | awk '{print $2}'sebagai cara untuk mendapatkan file terbesar di direktori. Dan itu berhasil, sampai Anda memiliki file dengan spasi di namanya.

OK, jadi bagaimana dengan ls -s | sort -n | tail -n 1 | sed 's/[^ ]* *[0-9]* *//'? Bekerja dengan baik sampai Anda memiliki file dengan baris baru di namanya.

Apakah menambahkan -quntuk lsargumen 's membantu ketika ada baris baru di nama file? Mungkin terlihat seperti itu, sampai Anda memiliki 2 file berbeda yang berisi karakter yang tidak dapat dicetak di tempat yang sama dalam nama file, dan kemudian lsoutput tidak membiarkan Anda membedakan mana dari yang terbesar. Lebih buruk lagi, untuk memperluas "?", Ia mungkin menggunakan shell-nya eval- yang akan menyebabkan masalah jika ia mengklik file bernama, misalnya,

foo`/tmp/malicious_script`bar

Apakah --quoting-style=shellmembantu (jika Anda lsbahkan mendukungnya)? Tidak, masih menampilkan? untuk karakter yang tidak dapat dicetak, jadi masih rancu mana dari beberapa pertandingan yang terbesar. --quoting-style=literal? Tidak, sama. --quoting-style=localeatau --quoting-style=cmungkin membantu jika Anda hanya perlu mencetak nama file terbesar dengan jelas, tetapi mungkin tidak jika Anda perlu melakukan sesuatu dengan file setelahnya - itu akan menjadi banyak kode untuk membatalkan penawaran dan kembali ke nama file yang sebenarnya jadi Anda dapat meneruskannya ke, katakanlah, gzip.

Dan di akhir semua pekerjaan itu, bahkan jika apa yang dia miliki aman dan benar untuk semua nama file yang mungkin, itu tidak dapat dibaca dan tidak dapat dipertahankan, dan bisa dilakukan dengan lebih mudah, aman, dan mudah dibaca dalam python atau perl atau ruby.

Atau bahkan menggunakan alat shell lain - dari atas kepala saya, saya pikir ini harus melakukan trik:

find . -type f -printf "%s %f\0" | sort -nz | awk 'BEGIN{RS="\0"} END{sub(/[0-9]* /, "", $0); print}'

Dan setidaknya harus se portabel seperti --quoting-styleini.

godlygeek
sumber
Oh benar tentang ukuran - saya mungkin bisa melakukannya jika saya mencoba - haruskah saya? Saya agak lelah atau semua ini - saya suka jawaban Anda karena Anda tidak mengatakan tidak bisa atau tidak atau tidak pernah benar-benar memberikan contoh mungkin mengapa tidak dan membandingkan bagaimana lagi - terima kasih.
mikeserv
Saya pikir jika Anda mencoba, Anda akan menemukan itu jauh lebih sulit daripada yang Anda pikirkan. Jadi, ya, saya sarankan mencoba. Saya akan senang untuk terus memberikan nama file yang akan rusak untuk Anda selama saya bisa memikirkannya. :)
godlygeek
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
terdon
@ mikeserv dan godlygeek, saya telah memindahkan utas komentar ini untuk mengobrol . Tolong jangan diskusi panjang seperti ini di komentar, untuk itulah obrolan.
terdon