Jawaban:
$ touch ./-c $ 'a \ n12 \ tb' foo $ du -hs * 0 a 12 b 0 foo 0 total
Seperti yang Anda lihat, -c
file diambil sebagai opsi untuk du
dan tidak dilaporkan (dan Anda melihat total
baris karena du -c
). Juga, file yang dipanggil a\n12\tb
membuat kita berpikir bahwa ada file yang dipanggil a
dan b
.
$ du -hs -- *
0 a
12 b
0 -c
0 foo
Itu lebih baik. Setidaknya saat -c
ini tidak diambil sebagai opsi.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Itu lebih baik. The ./
mencegah awalan -c
dari yang diambil sebagai pilihan dan tidak adanya ./
sebelum b
di output menunjukkan bahwa tidak ada b
file dalam sana, tapi ada file dengan karakter baris baru (tapi lihat di bawah 1 untuk penyimpangan lebih lanjut tentang itu).
Sebaiknya gunakan ./
awalan jika memungkinkan, dan jika tidak dan untuk data yang berubah-ubah, Anda harus selalu menggunakan:
cmd -- "$var"
atau:
cmd -- $patterns
Jika cmd
tidak mendukung --
untuk menandai akhir opsi, Anda harus melaporkannya sebagai bug kepada pembuatnya (kecuali jika itu pilihan dan didokumentasikan seperti untuk echo
).
Ada kasus di mana ./*
memecahkan masalah yang --
tidak. Contohnya:
awk -f file.awk -- *
gagal jika ada file yang dipanggil a=b.txt
di direktori saat ini (set variabel awk a
untuk b.txt
bukannya mengatakan untuk memproses file).
awk -f file.awk ./*
Tidak memiliki masalah karena ./a
bukan nama variabel awk yang valid, jadi ./a=b.txt
tidak diambil sebagai penugasan variabel.
cat -- * | wc -l
gagal jika ada file yang dipanggil -
dalam direktori saat ini, karena ia memerintahkan cat
untuk membaca dari stdin-nya ( -
khusus untuk sebagian besar utilitas pemrosesan teks dan ke cd
/ pushd
).
cat ./* | wc -l
tidak masalah karena ./-
tidak khusus untuk cat
.
Hal-hal seperti:
grep -l -- foo *.txt | wc -l
untuk menghitung jumlah file yang berisi foo
salah karena mengasumsikan nama file tidak mengandung karakter baris baru ( wc -l
menghitung karakter baris baru, yang dihasilkan oleh grep
untuk setiap file dan orang-orang di nama file itu sendiri). Anda sebaiknya menggunakan:
grep -l foo ./*.txt | grep -c /
(menghitung jumlah /
karakter lebih dapat diandalkan karena hanya ada satu per nama file).
Untuk rekursif grep
, trik yang setara adalah menggunakan:
grep -rl foo .//. | grep -c //
./*
mungkin memiliki beberapa efek samping yang tidak diinginkan.
cat ./*
menambahkan dua karakter lebih per file, sehingga akan membuat Anda mencapai batas ukuran maksimum argumen + lingkungan lebih cepat. Dan kadang-kadang Anda tidak ingin itu ./
dilaporkan dalam output. Seperti:
grep foo ./*
Akan menghasilkan:
./a.txt: foobar
dari pada:
a.txt: foobar
1 . Saya merasa harus mengembangkannya di sini, mengikuti diskusi dalam komentar.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Di atas, yang ./
menandai awal setiap file berarti kita dapat dengan jelas mengidentifikasi di mana setiap nama file dimulai (di ./
) dan di mana itu berakhir (di baris baru sebelum berikutnya ./
atau akhir output).
Artinya adalah bahwa output dari du ./*
, berlawanan dengan dari du -- *
) dapat diurai dengan andal, meskipun tidak dengan mudah dalam sebuah skrip.
Ketika output masuk ke terminal, ada banyak cara nama file yang bisa menipu Anda:
\r
memindahkan kursor ke awal baris, \b
memindahkan kursor ke belakang, \e[C
ke depan (di sebagian besar terminal) ...Ada karakter Unicode yang terlihat sama dengan garis miring di sebagian besar font
$ printf '\u002f \u2044 \u2215 \u2571 \u29F8\n'
/ ⁄ ∕ ╱ ⧸
(lihat bagaimana kelanjutannya di browser Anda).
Sebuah contoh:
$ touch x 'x ' $'y\bx' $'x\n0\t.\u2215x' $'y\r0\t.\e[Cx'
$ ln x y
$ du -hs ./*
0 ./x
0 ./x
0 ./x
0 .∕x
0 ./x
0 ./x
Banyak x
tapi y
tidak ada.
Beberapa alat seperti GNU
ls akan menggantikan karakter yang tidak dapat dicetak dengan tanda tanya (perhatikan bahwa ∕
(U + 2215) dapat dicetak) ketika output menuju terminal. GNU du
tidak.
Ada cara untuk membuat mereka mengungkapkan diri mereka sendiri:
$ ls
x x x?0?.∕x y y?0?.?[Cx y?x
$ LC_ALL=C ls
x x?0?.???x x y y?x y?0?.?[Cx
Lihat bagaimana ∕
menoleh ???
setelah kami memberi tahu ls
bahwa rangkaian karakter kami adalah ASCII.
$ du -hs ./* | LC_ALL=C sed -n l
0\t./x$
0\t./x $
0\t./x$
0\t.\342\210\225x$
0\t./y\r0\t.\033[Cx$
0\t./y\bx$
$
menandai akhir dari baris, sehingga kita dapat melihat "x"
vs "x "
, semua karakter yang tidak dapat dicetak dan karakter non-ASCII diwakili oleh urutan backslash (backslash sendiri akan diwakili dengan dua backslash) yang berarti tidak ambigu. Itu adalah GNU sed
, seharusnya sama di semua sed
implementasi yang sesuai dengan POSIX tetapi perhatikan bahwa beberapa sed
implementasi lama hampir tidak membantu.
$ du -hs ./* | cat -vte
0^I./x$
0^I./x $
0^I./x$
0^I.M-bM-^HM-^Ux$
(tidak standar tetapi cukup umum, juga cat -A
dengan beberapa implementasi). Yang satu itu membantu dan menggunakan representasi yang berbeda tetapi ambigu ( "^I"
dan <TAB>
ditampilkan sama misalnya).
$ du -hs ./* | od -vtc
0000000 0 \t . / x \n 0 \t . / x \n 0 \t .
0000020 / x \n 0 \t . 342 210 225 x \n 0 \t . / y
0000040 \r 0 \t . 033 [ C x \n 0 \t . / y \b x
0000060 \n
0000061
Itu standar dan tidak ambigu (dan konsisten dari implementasi ke implementasi) tetapi tidak mudah dibaca.
Anda akan melihat bahwa y
tidak pernah muncul di atas. Itu masalah yang sama sekali tidak terkait dengan du -hs *
yang tidak ada hubungannya dengan nama file tetapi harus dicatat: karena du
melaporkan penggunaan disk, itu tidak melaporkan tautan lain ke file yang sudah terdaftar (tidak semua du
implementasi berperilaku seperti itu meskipun ketika tautan keras terdaftar di baris perintah).
[a-z0-9.+-]
.
/tmp
) atau area dengan banyak mobil mahal ( $HOME
) dan bahkan lebih buruk untuk pergi ke situs T&J dan mengatakan itu selalu baik-baik saja untuk tidak mengunci mobil Anda tanpa menentukan dalam kondisi apa (di garasi yang terkunci, skrip Anda menulis dijalankan sendiri hanya pada mesin yang tidak terhubung ke jaringan apa pun atau penyimpanan yang dapat dilepas ...)
"b "
atau "a\bb"
) akan membodohi pengguna pada terminal tetapi bukan skrip yang mem-parsing output dari du ./*
. Saya mungkin harus menambahkan catatan tentang itu. Akan dilakukan besok. Perhatikan bahwa sebelumnya saya maksudkan hak istimewa dalam arti umum, tidak root
(meskipun berlaku root
tentu saja semua lebih ). baris baru diizinkan, mengabaikannya adalah bug. bug memiliki kebiasaan dieksploitasi. Anda harus mengukur risiko berdasarkan kasus per kasus. Praktek pengkodean yang baik dapat menghindari masalah dalam banyak kasus. Tentu saja di SE, kita harus meningkatkan kesadaran.
Tidak ada perbedaan antara a *
dan ./*
dalam hal file apa yang akan dicantumkan. Satu-satunya perbedaan adalah dengan bentuk ke-2, setiap file akan memiliki ./
awalan titik slash di depannya, yang biasanya berarti direktori saat ini.
Ingat bahwa .
direktori adalah notasi singkat untuk direktori saat ini.
$ ls -la | head -4
total 28864
drwx------. 104 saml saml 12288 Jan 23 20:04 .
drwxr-xr-x. 4 root root 4096 Jul 8 2013 ..
-rw-rw-r--. 1 saml saml 972 Oct 6 20:26 abcdefg
Anda dapat meyakinkan diri sendiri bahwa 2 daftar ini pada dasarnya adalah hal yang sama dengan menggunakan echo
untuk melihat apa shell akan mengembangkannya.
$ echo *
$ echo ./*
2 perintah ini akan mencantumkan semua file di direktori Anda saat ini.
Kami dapat membuat beberapa data palsu seperti:
$ touch file{1..5}
$ ll
total 0
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file1
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file2
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file3
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file4
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file5
Sekarang ketika kita menggunakan echo
perintah di atas kita melihat output berikut:
$ echo *
file1 file2 file3 file4 file5
$ echo ./*
./file1 ./file2 ./file3 ./file4 ./file5
Perbedaan ini mungkin tampak tidak perlu tetapi ada situasi di mana Anda ingin menjamin ke berbagai alat baris perintah Unix yang Anda berikan nama file kepada mereka melalui baris perintah, dan tidak lebih!
Seperti yang dijawab oleh @ Stephane , karena sifat karakter apa yang legal ketika menamai file & direktori di Unix, nama file berbahaya dapat dibangun yang memiliki efek samping yang tidak terduga ketika mereka diteruskan ke berbagai perintah Unix di baris perintah.
Begitu sering penggunaan ./
akan digunakan untuk membantu menjamin bahwa nama file yang diperluas dianggap sebagai nama file ketika diteruskan sebagai argumen untuk berbagai perintah Unix.