Perluasan bash brace setelah slash jalur

12

Saya mencoba menyalin file ke nama lain ke direktori yang sama menggunakan brace expansion. Saya menggunakan bash 4.4.18.

Inilah yang saya lakukan:

cp ~/some/dir/{my-file-to-rename.bin, new-name-of-file.bin}

tapi saya mendapatkan kesalahan ini:

cp: cannot stat '/home/xyz/some/dir/{my-file-to-rename.bin,': No such file or directory

Bahkan ekspansi brace sederhana seperti ini memberi saya kesalahan yang sama:

cp {my-file-to-rename.bin, new-name-of-file.bin}

Apa yang saya lakukan salah?

nanangarsyad
sumber

Jawaban:

29

The ekspansi brace sintaks menerima koma, tetapi tidak menerima spasi setelah koma. Dalam banyak bahasa pemrograman, spasi setelah koma adalah hal biasa, tetapi tidak di sini. Di Bash, keberadaan ruang tanpa tanda kutip mencegah ekspansi brace dilakukan.

Hapus ruang, dan itu akan berhasil:

cp ~/some/dir/{my-file-to-rename.bin,new-name-of-file.bin}

Meskipun sama sekali tidak diperlukan, perhatikan bahwa Anda dapat memindahkan trailing di .binluar kawat gigi:

cp ~/some/dir/{my-file-to-rename,new-name-of-file}.bin

Jika Anda ingin menguji efek ekspansi brace, Anda dapat menggunakan echoatau printf '%s ', atau printfdengan format string apa pun yang Anda inginkan, untuk melakukannya. (Secara pribadi saya hanya menggunakan echountuk ini, ketika saya berada di Bash, karena Bash's echobuiltin tidak memperluas urutan escape secara default, dan dengan demikian cukup cocok untuk memeriksa perintah apa yang sebenarnya akan berjalan.) Misalnya:

ek@Io:~$ echo cp ~/some/dir/{my-file-to-rename,new-name-of-file}.bin
cp /home/ek/some/dir/my-file-to-rename.bin /home/ek/some/dir/new-name-of-file.bin
Eliah Kagan
sumber
23

string{foo, bar}bukan penjepit ekspansi; itu dua kata string{foo,dan bar}. Untuk menggunakan ekspansi brace, kawat gigi harus berada dalam kata yang sama. Anda harus menghapus ruang ekstra jika Anda tidak membutuhkannya, atau mengutipnya jika Anda memang membutuhkannya:

$ printf "%s\n" aa{bb, cc}
aa{bb,
cc}
$ printf "%s\n" aa{bb,cc}
aabb
aacc
$ printf "%s\n" aa{bb," cc"}
aabb
aa cc
ilkkachu
sumber
+1 untuk contoh yang jelas, dan terima kasih. Saya akan memberikan suara saya ketika reputasi saya cukup. :)
nanangarsyad
Ada hal lain yang harus disebutkan tentang "kata-kata shell". . .seperti bagaimana shell tahu di mana harus memisahkan kata-kata;)
Sergiy Kolodyazhnyy
-1

Bash memperlakukan ruang itu seperti yang lainnya. Sebagai IFS, Pemisah Bidang Internal. Ini digunakan untuk pemisahan kata setelah ekspansi dan untuk memecah baris menjadi kata-kata dengan perintah read builtin.

Shell memperlakukan setiap karakter IFS sebagai pembatas, dan membagi hasil ekspansi lainnya menjadi kata-kata pada karakter ini. Jika IFS tidak disetel, atau nilainya tepat, default, lalu urutan,, dan pada awal dan akhir hasil ekspansi sebelumnya diabaikan, dan setiap urutan karakter IFS tidak di awal atau akhir berfungsi untuk membatasi kata-kata. Jika IFS memiliki nilai selain default, maka urutan spasi dan tab karakter spasi diabaikan di awal dan akhir kata, selama karakter spasi dalam nilai IFS (karakter spasi IFS). Setiap karakter dalam IFS yang bukan spasi IFS, bersama dengan karakter spasi IFS yang berdekatan, membatasi bidang. Urutan karakter spasi putih IFS juga diperlakukan sebagai pembatas. Jika nilai IFS adalah nol,
-bash (1)

Dengan memasukkan pembatas, tidak terhapus, maka Anda memberi tahu bash perintah dan argumen Anda adalah:

  1. "cp"
  2. "~ / some / dir / {my-file-to-rename.bin,"
  3. "new-name-of-file.bin}"

Seandainya Anda memiliki kutipan atau pelarian "\", Anda akan memiliki:

  1. "cp"
  2. "~ / some / dir / {my-file-to-rename.bin, \ new-name-of-file.bin}"

Yang juga tidak akan seperti yang Anda inginkan, kecuali "new-name-of-file.bin" adalah nama file baru yang Anda inginkan. Termasuk ruang. Saat ekspansi brace terjadi terlebih dahulu, dan kemudian ekspansi tilde, bash akan mengeksekusi:

  1. "cp"
  2. "/path/to/home/some/dir/my-file-to-rename.bin"
  3. "/ path / ke / home / some / dir / \ new-name-of-file.bin"

Hanya dengan menghapus ruang akan memperbaiki semua itu.

cde
sumber
5
Ini sebagian besar baik, tetapi Anda tampaknya mengatakan pemisahan kata terjadi cp ~/some/dir/{my-file-to-rename.bin, new-name-of-file.bin}dan IFSmempengaruhi hasilnya. Tidak juga begitu. Di sini, ruang adalah metacharacter dalam tokenization ( langkah 2 ). Lihat 3.5.7 tentang kapan pemisahan terjadi. Cobalah IFS=xkemudian printf '[%s]\n' {a,b} printf '[%s]\n' {a, b} printf '[%s]\n' {a,xb} printf '[%s]\n' {a, xb}.
Eliah Kagan