bash menggabungkan ekspansi wildcard dengan ekspansi brace

8

Saya mencoba untuk memperluas string yang melibatkan wildcard dan koleksi ekstensi yang ditentukan dalam kurung kurawal. Tampaknya tidak ada yang berfungsi seperti contoh di bawah ini. variabel firstListmengembang dengan baik, tetapi tidak ada secondList, thirdListatau fourthListmengembang dengan baik. Saya juga sudah mencoba berbagai versi evaltetapi tidak ada yang berhasil juga. Bantuan apa pun akan dihargai

#!/bin/bash
touch a.ext1
touch b.ext1
firstList='*.ext1'
ls  $firstList
touch a.ext2
touch b.ext2
secondList='*.{ext1,ext2}'
ls $secondList 
ls '$secondList'
ls "$secondList"
thirdList=*.{ext1,ext2}
ls $thirdList  
ls '$thirdList'
ls "$thirdList"
fourthList="*.{ext1,ext2}"
ls $fourthList
ls '$fourthList'
ls "$fourthList"
Leo Simon
sumber
fwiw eval ls $secondListbekerja dengan baik di sini ... apa yang ingin kamu capai?
don_crissti
Anda perlu memeriksa urutan ekspansi bash - ekspansi brace terjadi sebelum ekspansi paramater. Untuk mendapatkan efek yang Anda harapkan, Anda perlu evalmendapatkan putaran ke-2 ekspansi.
glenn jackman
@glennjackman Ada solusi tanpa eval. Lihatlah solusi kedua di akhir jawaban saya .

Jawaban:

7

The shell memperluas *hanya jika un-dikutip, setiap mengutip berhenti ekspansi dengan shell.

Juga, ekspansi brace perlu tanda kutip untuk diperluas oleh shell.

Karya ini (mari kita gunakan gema untuk melihat apa yang dilakukan shell):

$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

Bahkan jika ada file dengan beberapa nama lain:

$ touch {a,b}.{ext1,ext2} {c,d}.{ext3,ext4} none
ls
a.ext1  a.ext2  b.ext1  b.ext2  c.ext3  c.ext4  d.ext3  d.ext4  none

$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

Mengapa itu berhasil?

Penting bagi kita untuk memahami mengapa itu berhasil. Itu karena urutan ekspansi. Pertama "ekspansi Brace" dan kemudian (yang terakhir) "Pathname Expansion" (alias glob-expansion).

Brace --> Parameter (variable) --> Pathname

Kami dapat mematikan "Perluasan jalur" sejenak:

$ set -f
$ echo *.{ext1,ext2}
*.ext1 *.ext2

"Perluasan Pathname" menerima dua argumen: *.ext1dan *.ext2.

$ set +f
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

Masalahnya adalah kita tidak bisa menggunakan variabel untuk ekspansi brace.
Telah dijelaskan beberapa kali sebelumnya untuk menggunakan variabel di dalam "Ekspansi Brace"

Untuk memperluas "Ekspansi Brace" yang merupakan hasil dari "Ekspansi Variabel", Anda harus mengirimkan kembali baris perintah ke shell dengan eval.

$ list={ext1,ext2}
$ eval echo '*.'"$list"

Penjepit -> Variabel -> Glob || -> Brace -> Variabel -> Glob
........ dikutip di sini -> ^^^^^^ || eval ^^^^^^^^^^^^^^^^^^^^^^^^^

Nilai nama file tidak menimbulkan masalah eksekusi untuk eval:

$ touch 'a;date;.ext1'
eval echo '*.'"$list"
a;date;.ext1 a.ext1 b.ext1 a.ext2 b.ext2

Tetapi nilai dari $listbisa tidak aman. Namun, nilai $listdiatur oleh penulis skrip. Penulis skrip mengendalikan eval: Hanya saja jangan gunakan nilai yang ditetapkan secara eksternal untuk $list. Coba ini:

#!/bin/bash
touch {a,b,c}.ext{1,2}
list=ext{1,2}
eval ls -l -- '*.'"$list"

Alternatif yang lebih baik.

Alternatif (tanpa eval) adalah menggunakan Bash "Extended Patterns" :

#!/bin/bash
shopt -s extglob
list='@(ext1|ext2)'
ls -- *.$list

Catatan: Perlu diketahui bahwa kedua solusi (eval dan pola) (seperti tertulis) aman untuk nama file dengan spasi atau baris baru. Tetapi akan gagal untuk $listdengan spasi, karena $listtanda kutip atau tanda kutip menghapus tanda kutip.

Komunitas
sumber
Ya, globbing diperpanjang
glenn jackman
2

Mempertimbangkan:

secondList='*.{ext1,ext2}'
ls $secondList 

Masalahnya adalah ekspansi brace dilakukan sebelum ekspansi variabel . Itu berarti bahwa, di atas, ekspansi brace tidak pernah dilakukan.

Ini karena, ketika bash pertama kali melihat baris perintah, tidak ada kawat gigi. Setelah secondListdiperluas, sudah terlambat.

Berikut ini akan berfungsi:

$ s='*'
$ ls $s.{ext1,ext2}
a.ext1  a.ext2  b.ext1  b.ext2

Di sini, baris perintah memiliki kawat gigi sehingga ekspansi kawat gigi dapat dilakukan sebagai langkah pertama. Setelah itu, nilai $ssubstitusi dalam ( ekspansi variabel ), dan ekspansi pathname terakhir dilakukan.

Dokumentasi

man bash menjelaskan urutan ekspansi:

Urutan ekspansi adalah: ekspansi brace; ekspansi tilde, ekspansi parameter dan variabel, ekspansi aritmatika, dan penggantian perintah (dilakukan dengan cara kiri-ke-kanan); pemisahan kata; dan perluasan pathname.

John1024
sumber