Bagaimana saya bisa membersihkan atau keluar dari jalur absolut yang dikembalikan oleh realpath atau readlink?

9

realpathdan readlinkmengembalikan jalur absolut:

+akiva@X230:~$ realpath ZannaIsAwesome
/home/akiva/ZannaIsAwesome

Jalan seperti itu mudah untuk dihadapi. Namun, sesuatu seperti ini akan memiliki beberapa masalah:

masukkan deskripsi gambar di sini

Sebagai contoh:

masukkan deskripsi gambar di sini

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 $bacontidak akan berfungsi seperti yang diharapkan.

Apa yang bisa saya lakukan untuk membersihkan jalur absolut itu sehingga akan bekerja dengan perintah lain?

Akiva
sumber
3
Selalu kutip variabel Anda:vim "$bacon"
steeldriver
@steeldriver Kenapa begitu?
Akiva
3
Karena jika tidak, Anda masuk ke situasi seperti ini :)
terdon

Jawaban:

12

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 pullingATerdonke 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 vimlainnya. 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 lsdijalankan 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 mkdircontoh 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|\|\|0rthdi 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 /, mkdirmenganggap 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:

  1. Bagaimana saya bisa menemukan dan menangani nama file dengan aman yang mengandung baris baru, spasi atau keduanya? : Salah satu FAQ tentang Wiki Grey Cat.

  2. 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.

  3. 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.

  4. Kapan perlu mengutip ganda? : Lebih lanjut tentang kutipan dan variabel dan, khususnya, beberapa kasus di mana Anda tidak perlu mengutipnya

terdon
sumber