realpath
dan readlink
mengembalikan jalur absolut:
+akiva@X230:~$ realpath ZannaIsAwesome
/home/akiva/ZannaIsAwesome
Jalan seperti itu mudah untuk dihadapi. Namun, sesuatu seperti ini akan memiliki beberapa masalah:
Sebagai contoh:
Jadi nama seperti ini perlu disanitasi agar dapat mengumpankannya ke perintah lain. Usecase mungkin seperti ini:
+a@X230:~/\e[92mM@r|< $hu+'|'|_e|\|\|0rth [`-_-"]$ bacon=$(realpath pullingATerdon)
+a@X230:~$ vim $bacon
Tak perlu dikatakan, vim $bacon
tidak akan berfungsi seperti yang diharapkan.
Apa yang bisa saya lakukan untuk membersihkan jalur absolut itu sehingga akan bekerja dengan perintah lain?
command-line
bash
paths
Akiva
sumber
sumber
vim "$bacon"
Jawaban:
Cara melakukannya dengan benar
Pertama-tama, selalu kutip variabel Anda . Apa yang Anda coba lakukan berfungsi dengan baik jika Anda mengutipnya dengan benar:
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:Itu gagal, seperti yang diharapkan. Tetapi, jika sekarang kita mengutipnya dengan benar:
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 yangvim
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 bersamaset +x
), yang menyebabkan shell mencetak setiap perintah yang akan dijalankan sebelum menjalankannya. nyalakan pesan debug shell denganset -x
: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:Sekali lagi, itu akan mencoba membuat tiga direktori, bukan satu, karena pemisahan kata. Pertama, itu membuat (berhasil) direktori
/home/terdon/foo/\e[92mM@r|<
:Kemudian, juga berhasil, membuat direktori yang disebut
+'|'|_e|\|\|0rth
di direktori Anda saat ini:Dan kemudian, ia mencoba membuat direktori
[`-_-"]/pullingATerdon
. Ini gagal karenamkdir
, secara default, tidak membuat subdirektori (itu bisa, jika Anda menjalankannya dengan-p
):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:Untuk memperjelas, inilah yang terjadi jika Anda memberikan bola yang cocok dengan sesuatu:
Tanda kutip
[p*]
diperluas ke daftar nama file yang cocok (hanya satu, dalam hal ini) dan itulah yang diteruskan keecho
. 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:
Itu akan bekerja untuk nama file apa pun. Tidak peduli apa yang terjadi mengandung. Dalam contoh kami di atas, Anda bisa melakukan:
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
sumber