Pergi ke direktori menggunakan variabel bash tidak berfungsi ketika nama direktori memiliki spasi

11

Katakanlah saya ingin menyimpan perintah berikut dalam sebuah variabel

cd "/cygdrive/c/Program Files/"

Jadi saya melakukan ini

dir="cd \"/cygdrive/c/Program Files/\""

Itu seharusnya menyimpan perintah untuk menavigasi ke direktori Program Files, jadi ketika saya mengetik $ dir itu membawa saya ke direktori itu. Untuk memeriksa apakah kutipan telah lolos dengan benar, saya mengetik

echo $dir

yang memberi saya

cd "/cygdrive/c/Program Files/"

Jadi semuanya harus bekerja dengan baik. Namun, saat saya mengetik,

$dir

saya mendapat

bash: cd: "/cygdrive/c/Program: No such file or directory

Apa yang saya lakukan salah? Saya menggunakan Cygwin, tetapi saya menganggap masalah ini berlaku untuk bash secara umum.

gsingh2011
sumber
Coba hapus tanda kutip di sekitar nama direktori dan hindari ruang dengan backslash.
Mike Fitzpatrick

Jawaban:

8

Jawaban singkat: lihat BashFAQ # 050 ("Saya mencoba menempatkan perintah dalam variabel, tetapi kasus kompleks selalu gagal!").

Jawaban panjang: ketika bash mem-parsing sebuah perintah, ia mem-parsing tanda kutip sebelum ia menggantikan variabel; itu tidak pernah kembali dan mem-parsing tanda kutip dalam nilai-nilai variabel, sehingga mereka akhirnya tidak melakukan sesuatu yang berguna. Menggunakan gema untuk memeriksa perintah benar-benar menyesatkan karena itu menunjukkan perintah setelah diuraikan; jika Anda ingin melihat apa yang sebenarnya dieksekusi, gunakan set -xperintah print shell saat dieksekusi, atau gunakan printf "%q " $dir; echosebagai ganti gema biasa.

Jika Anda ingin menyimpan perintah yang kompleks (mis. Yang memiliki spasi atau karakter khusus lainnya di "kata"), Anda harus meletakkannya dalam array bukan variabel teks sederhana, dan kemudian perluas dengan idiom `" $ { array [@]} ", seperti ini:

dir=(cd "/cygdrive/c/Program Files/")
"${dir[@]}"

Sekarang, jika tujuannya adalah untuk membuat steno yang mudah diketik, ini jelas bukan cara yang tepat. Gunakan alias shell atau fungsi sebagai gantinya:

alias dir='cd "/cygdrive/c/Program Files/"'
dir

atau

dir() { cd "/cygdrive/c/Program Files/"; }
dir
Gordon Davisson
sumber
7

Ketika bash mengembang $dir, itu hanya melakukan pemisahan kata dan penggumpalan di atasnya. Kata membelah menghasilkan tiga kata: cd, "/cygdrive/c/Programdan Files/". Maka perintah untuk mengeksekusi adalah cddengan dua argumen; cdhanya melihat argumen pertamanya "/cygdrive/c/Program,, yang bukan direktori yang ada.

Jika Anda ingin melakukan evaluasi shell penuh pada konten variabel, gunakan eval:

eval "$dir"

Perhatikan bahwa Anda memerlukan tanda kutip ganda di sekitar $dir, jika tidak, pemisahan kata pertama-tama akan dilakukan, kemudian evalakan menggabungkan argumennya dengan spasi. Ini akan terjadi untuk bekerja di sini, tetapi akan memburuk secara umum (misalnya jika ada dua spasi berturut-turut dalam nama file).

Namun, string bukan cara yang tepat untuk menyimpan perintah shell yang ingin Anda jalankan. Kecuali Anda memiliki persyaratan yang tidak biasa, Anda harus menggunakan fungsi sebagai gantinya:

dir () {
  cd "/cygdrive/c/Program Files/"
}

Jika Anda benar-benar tertarik dalam mengetik $diruntuk beralih ke direktori tertentu dan ini bukan hanya sebuah contoh sederhana, karena pesta 4, dimasukkan shopt -s autocddalam Anda .bashrcset dan

dir="/cygdrive/c/Program Files/"

setelah itu Anda bisa mengetikkan saja "/cygdrive/c/Program Files/"atau "$dir"pada prompt shell untuk beralih ke direktori itu. Anda masih membutuhkan tanda kutip ganda di sekitar $dir; jika Anda tidak suka itu, gunakan zsh bukannya bash.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
6

Menyimpan perintah dalam variabel umumnya bukan ide yang baik. Itulah fungsi dan alias untuk apa.

Daripada

dir="cd \"/cygdrive/c/Program Files/\""

coba salah satu dari ini:

dir="/cygdrive/c/Program Files"
...
cd "$dir"

atau

dir() { cd "/cygdrive/c/Program Files"; }
dir

atau

alias dir='cd "/tmp/Program Files"'
...
dir

Bentuk pertama, menyimpan nama direktori dalam variabel, berarti mengetik lebih sedikit, tetapi lebih jelas ketika Anda menjalankan perintah yang Anda lakukan cd. Yang kedua paling dekat dengan apa yang ingin Anda capai. (Saya tidak menggunakan alias banyak di bash; Saya tidak benar-benar yakin apa kelebihan mereka daripada fungsi.)

EDIT:

Dan dirmungkin nama yang buruk untuk alias atau fungsi, karena ada perintah yang ada dengan nama itu (itu pada dasarnya adalah versi ls, setidaknya jika Anda memiliki GNU coreutils).

Keith Thompson
sumber
upvote untukStoring commands in variables is generally not a good idea. That's what functions and aliases are for.
Rob