Saya secara konsisten melihat jawaban yang mengutip tautan ini yang menyatakan dengan pasti, "Jangan parsing ls
!" Ini menggangguku karena beberapa alasan:
Tampaknya informasi dalam tautan tersebut telah diterima secara grosir dengan sedikit pertanyaan, meskipun saya dapat memilih setidaknya beberapa kesalahan dalam membaca santai.
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. ...ls
memisahkan nama file dengan baris baru. Ini bagus sampai Anda memiliki file dengan baris baru dalam namanya. Dan karena saya tidak tahu implementasi apa punls
yang memungkinkan Anda untuk mengakhiri nama file dengan karakter NUL alih-alih baris baru, ini membuat kami tidak dapat memperoleh daftar nama file dengan amanls
.
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 ls
implementasi 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
ls
kadang-kadang data data file Anda rusak (dalam kasus kami, itu mengubah\n
karakter di antara kata "a" dan "baris baru" menjadi tanda tanya? ......
Jika Anda hanya ingin mengulang semua file dalam direktori saat ini, gunakan
for
loop dan glob:
for f in *; do
[[ -e $f ]] || continue
...
done
Penulis menyebutnya mengacaukan nama file ketika ls
mengembalikan 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 -1
dan -q
ls
operan 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 ls
keluarannya 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 zsh
itu 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_SPLIT
variabel saat menggunakan zsh
dan 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 -u
daftar untuk hasil yang unik. Ini aman karena ls
telah 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 \0
nol - /
karakter ASCII adalah satu-satunya byte yang dilarang dalam pathname. Saya kesampingkan gumpalan di sini dan sebagai gantinya menggabungkan opsi POSIX yang ditentukan -d
untuk ls
dan juga -exec $cmd {} +
konstruksi POSIX yang ditentukan untuk find
. Karena find
hanya 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 ls
yang 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.
time bash -c 'for i in {1..1000}; do ls -R &>/dev/null; done'
= 3.18s vstime bash -c 'for i in {1..1000}; do echo **/* >/dev/null; done'
= 1.28sstat
jawaban saya, karena sebenarnya memeriksa bahwa setiap file ada. Bit Anda di bagian bawah dengansed
hal itu tidak berfungsi.ls
pada 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.ls
output 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.parsing ls is bad
. Melakukanfor something in $(command)
dan mengandalkan pemisahan kata untuk mendapatkan hasil yang akurat adalah buruk bagi sebagian besarcommand's
yang tidak memiliki output sederhana.Jawaban:
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
ls
andal, 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
ls
sepertinya 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:Ini tidak memiliki masalah apa pun dengan karakter yang tidak biasa dalam nama file - outputnya ambigu dengan cara yang sama dengan output
ls
yang ambigu, tetapi itu tidak masalah dalam program "nyata" (sebagai lawan dari demo seperti ini), yang akan gunakan hasilos.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:
sumber
for in | for in
berbicara tentang rekursi? Saya tidak yakin. Bahkan jika itu tidak lebih dari satu, bukan? Inilah satu-satunya jawaban yang masuk akal bagi saya sejauh ini.for
-loopsos.walk
melakukan beberapa pengangkatan serius di belakang layar, tetapi Anda tidak perlu khawatir tentang hal itu lebih daripada Anda harus khawatir tentang bagaimanals
ataufind
bekerja secara internal.os.walk
mengembalikan 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 adalahFile::Find
, jika itu membantu.ls
output.Tautan itu banyak dirujuk karena informasinya benar-benar akurat, dan sudah lama ada di sana.
ls
mengganti karakter yang tidak dapat dicetak dengan karakter glob ya, tetapi karakter tersebut tidak ada dalam nama file yang sebenarnya. Mengapa ini penting? 2 alasan:Sebagai contoh:
Perhatikan bagaimana kita memiliki 2 file yang terlihat persis sama. Bagaimana Anda akan membedakan mereka jika keduanya diwakili
a?b
?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:
Perhatikan bagaimana
xxd
output menunjukkan yang$file
berisi karakter mentah\t
dan\n
, bukan?
.Jika Anda menggunakan
ls
, Anda mendapatkan ini sebagai gantinya:"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:
Saya telah membuat direktori dengan banyak nama file:
Ketika saya menjalankan kode Anda, saya mendapatkan ini:
Ke mana sisa file pergi?
Mari kita coba ini sebagai gantinya:
Sekarang mari kita gunakan gumpalan yang sebenarnya:
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:
Hasil yang sangat berbeda dengan kode Anda:
Dengan glob shell, ia bekerja dengan sangat baik:
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".
ls
mengembalikan 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:
Kode hex adalah karakter UTF-8 NBSP.
sumber
ls
. Saya juga meminta Anda menguji kode Anda karena tidak berfungsi. Apa yang harus dilakukan zsh dengan semua ini?Mari kita coba dan sederhanakan sedikit:
Lihat? Itu sudah salah di sana. Ada 3 file tetapi bash melaporkan 4. Ini karena
set
sedang diberikan gumpalan yang dihasilkan olehls
diperluas oleh shell sebelum diteruskan keset
. Itu sebabnya Anda mendapatkan:Atau, jika Anda lebih suka:
Di atas dijalankan
bash 4.2.45
.sumber
ls -1qRi | grep -o '^ *[0-9]*'
- itu adalahls
hasil parsing , man, dan ini adalah cara tercepat dan terbaik yang saya tahu untuk mendapatkan daftar nomor inode.Output dari
ls -q
bukan 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 lolosls -q
.Jika Anda memperlakukan
ls -1q
output ada satu set gumpalan dan mengembangkannya, Anda tidak hanya akan mendapatkanx
dua 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
Jawabannya sederhana: Kasus khusus
ls
Anda harus menangani lebih besar daripada manfaat apa pun yang mungkin. Kasing khusus ini dapat dihindari jika Anda tidak menguraikanls
output.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
ls
melakukan 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 menggunakanls
dalam 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 .
sumber
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
uniq
untuk memfilter duplikat, akibatnya kami tidak dapat menghasilkan hasil dengan malas. JadiO(1)
vsO(n)
atau keduanya milikiO(n)
.Efisiensi waktu: Kasus terbaik
uniq
menggunakan pendekatan hashmap sehingga kami masih memilikiO(n)
algoritma dalam jumlah elemen yang dibeli , mungkin meskipun ituO(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\n
yang akan menghasilkan glob untuk??
jadi cocokkan setiap 2 karakter file dalam daftar. Lucunya jika Anda memiliki file lain\n\r
yang 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.
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:
Sekarang mari kita coba solusi pertama Anda, karena saya benar-benar tidak bisa mendapatkan ini
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
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.
sumber
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
ls
output» atau «Anda tidak boleh parsels
output», 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
ls
output, 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:
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
Goto
sebagai 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:
di mana, tentu saja Anda dapat mengganti "
ls
output tidak pernah diuraikan " sebagai pengganti $ {ATURAN}. :)Oh ya! Apa Tentang Parsing
ls
output?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
ls
ide 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
sumber
ls
!' adalah saran yang benar: 1. tunjukkan (untuk kepuasan Anda) bahwa setiap kasus penggunaan di mana seseorang mungkin mengurails
output 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.ls
adalah utilitas komputer - Anda dapat menguraikan output komputer.Apakah mungkin untuk mengurai output
ls
dalam kasus-kasus tertentu? Tentu. Ide mengekstraksi daftar nomor inode dari direktori adalah contoh yang bagus - jika Anda tahu bahwals
dukungan 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 darils -Rai1q
keluaran 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 manals
output 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, karenals
merupakan 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
-q
untukls
argumen '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 kemudianls
output tidak membiarkan Anda membedakan mana dari yang terbesar. Lebih buruk lagi, untuk memperluas "?", Ia mungkin menggunakan shell-nyaeval
- yang akan menyebabkan masalah jika ia mengklik file bernama, misalnya,Apakah
--quoting-style=shell
membantu (jika Andals
bahkan 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=locale
atau--quoting-style=c
mungkin 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:
Dan setidaknya harus se portabel seperti
--quoting-style
ini.sumber