Cara melakukannya dengan benar
Pertama-tama, selalu kutip variabel Anda . Apa yang Anda coba lakukan berfungsi dengan baik jika Anda mengutipnya dengan benar:
$ pwd
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]
$ ls
pullingATerdon
Saya telah menyimpan nama file aneh yang Anda pilih ( walaupun saya tidak tahu mengapa Anda memilihnya ) demi konsistensi.
Sekarang, mari tetapkan path pullingATerdon
ke variabel dan kemudian coba buka file:
$ bacon="$(realpath pullingATerdon)"
$ echo "$bacon"
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]/pullingATerdon
$ ls $bacon
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':
Itu gagal, seperti yang diharapkan. Tetapi, jika sekarang kita mengutipnya dengan benar:
$ ls -l "$bacon"
-rw-r--r-- 1 terdon terdon 0 Mar 14 23:15 '/home/terdon/foo/\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]/pullingATerdon'
Ini berfungsi seperti yang diharapkan. Dan ya, Anda juga dapat membuka jalur dalam editor (yang benar): emacs "$bacon"
akan berfungsi dengan baik. OK, begitu juga dengan yang vim
lainnya. Pilihan editor Anda, meskipun tidak menguntungkan, tidak relevan.
Kenapa milikmu gagal
Cara cepat untuk melacak apa yang sebenarnya terjadi dalam kasus Anda adalah dengan menggunakan set -x
(matikan lagi bersama set +x
), yang menyebabkan shell mencetak setiap perintah yang akan dijalankan sebelum menjalankannya. nyalakan pesan debug shell dengan set -x
:
$ set -x
$ /bin/ls $bacon
+ ls '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':
Itu menunjukkan bahwa ls
dijalankan dengan tiga argumen yang terpisah: '/home/terdon/foo/\e[92mM@r|<'
, '+'\''|'\''|_e|\|\|0rth'
dan '[`-_-"]/pullingATerdon'
. Ini terjadi karena shell melakukan pemisahan kata dan ekspansi glob pada string yang tidak dikutip. Dalam kasus ini, masalahnya adalah pemisahan kata, karena shell melihat spasi di path dan membaca setiap string yang dipisahkan spasi sebagai argumen terpisah.
The mkdir
contoh adalah sedikit berbeda tapi itu karena Anda menunjukkan kepada kita pesan kesalahan dari kedua doa dari perintah. Saya kira Anda pernah mencobanya sekali, dan kemudian menjalankannya untuk kedua kalinya untuk mendapatkan output untuk pertanyaan Anda. Pertama kali Anda menjalankannya, akan terlihat seperti ini:
$ mkdir $(realpath pullingATerdon)
++ realpath pullingATerdon
+ mkdir '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
mkdir: cannot create directory ‘[`-_-"]/pullingATerdon’: No such file or directory
Sekali lagi, itu akan mencoba membuat tiga direktori, bukan satu, karena pemisahan kata. Pertama, itu membuat (berhasil) direktori /home/terdon/foo/\e[92mM@r|<
:
$ ls -l /home/terdon/foo/
total 8
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|<'
drwxr-xr-x 3 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]'
Kemudian, juga berhasil, membuat direktori yang disebut +'|'|_e|\|\|0rth
di direktori Anda saat ini:
$ ls -l
total 4
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:37 '+'\''|'\''|_e|\|\|0rth'
-rw-r--r-- 1 terdon terdon 0 Mar 15 00:36 pullingATerdon
Dan kemudian, ia mencoba membuat direktori [`-_-"]/pullingATerdon
. Ini gagal karena mkdir
, secara default, tidak membuat subdirektori (itu bisa, jika Anda menjalankannya dengan -p
):
$ mkdir baz/bar
mkdir: cannot create directory ‘baz/bar’: No such file or directory
Karena string Anda yang tidak dikutip mengandung a /
, mkdir
menganggap bahwa jalur dua direktori, mencoba menemukan yang teratas, dan gagal.
Itu sebabnya gagal, tetapi yang terjadi lebih rumit. String yang Anda digunakan sebenarnya segumpal shell, khususnya berbagai gumpal , yang cocok dengan semua file dalam direktori saat ini yang namanya salah satu dari 5 karakter `
, -
, _
atau "
. Karena Anda tidak memiliki file seperti itu di direktori Anda saat ini, glob tidak cocok dengan apa pun dan, seperti perilaku default di bash, mengembalikan dirinya sendiri:
$ echo "[\`-_-\"]/pullingATerdon" ## some escaping is needed here
+ echo '[`-_-"]/pullingATerdon' ## but it echoes the right thing
[`-_-"]/pullingATerdon ## and matches nothing, so returns itself.
Untuk memperjelas, inilah yang terjadi jika Anda memberikan bola yang cocok dengan sesuatu:
$ echo [p]* ## any filename starting with a p
pullingATerdon
$ echo "[p]*" ## the string "[p]*"
[p]*
Tanda kutip [p*]
diperluas ke daftar nama file yang cocok (hanya satu, dalam hal ini) dan itulah yang diteruskan ke echo
. Namun alasan lain mengapa Anda harus mengutip semua hal.
Akhirnya, kesalahan aktual yang Anda tunjukkan adalah dari kedua kalinya Anda menjalankan perintah dan gagal pada langkah pertama, ketika mencoba membuat /home/terdon/foo/\e[92mM@r|<
, karena doa sebelumnya telah membuat direktori itu.
Lebih umum, setiap kali Anda menemukan diri Anda bekerja dengan nama file yang sewenang-wenang, selalu gunakan gumpalan shell. Hal-hal seperti ini:
for file in *; do command "$file"; done
Itu akan bekerja untuk nama file apa pun. Tidak peduli apa yang terjadi mengandung. Dalam contoh kami di atas, Anda bisa melakukan:
emacs /home/terdon/*92mM*/pullingATerdon
Glob yang mengidentifikasi file target secara unik akan dilakukan. Dengan begitu, Anda tidak perlu khawatir tentang karakter khusus dan membiarkan shell menangani mereka.
Beberapa referensi yang bermanfaat:
Bagaimana saya bisa menemukan dan menangani nama file dengan aman yang mengandung baris baru, spasi atau keduanya? : Salah satu FAQ tentang Wiki Grey Cat.
Implikasi keamanan dari lupa mengutip variabel dalam bash / POSIX shells : post yang sama yang saya referensikan pada awal jawaban ini. Penjelasan yang bagus dan sangat rinci dari semua hal yang bisa salah jika Anda gagal mengutip variabel shell Anda dengan benar.
Mengapa skrip shell saya tercekik di spasi putih atau karakter khusus lainnya? : semua yang ingin Anda ketahui tentang penanganan nama file yang sewenang-wenang di shell.
Kapan perlu mengutip ganda? : Lebih lanjut tentang kutipan dan variabel dan, khususnya, beberapa kasus di mana Anda tidak perlu mengutipnya
vim "$bacon"