Bagaimana saya bisa menggunakan $ variabel dalam ekspansi brace shell dari urutan?

33

Saya ingin menggunakan $var inekspansi brace shell dengan kisaran, di bash. Sederhananya {$var1..$var2}tidak bekerja, jadi saya pergi "lateral" ...

Berikut ini berfungsi, tetapi agak kumuh.

# remove the split files
echo rm foo.{$ext0..$extN} rm-segments > rm-segments
source rm-segments

Apakah ada cara yang lebih "normal"?

Peter.O
sumber

Jawaban:

26

Anda mungkin ingin mencoba:

eval rm foo.{$ext0..$extN}

Tidak yakin apakah ini jawaban terbaik, tetapi tentu saja satu.

pindahkan
sumber
Sial! :( Saya tidak bisa melihat yang jelas ... Terima kasih :)
Peter.O
4
Dalam bash, ekspansi {} terjadi sebelum $ ekspansi, jadi saya tidak berpikir ada cara lain untuk melakukan ini selain menggunakan eval atau trik lain untuk menyebabkan dua melewati ekspresi.
mattdm
6
Saya baru "mendapatkannya"! ... Tidak ada ekspansi brace dengan {$one..$three}, karena itu bukan bentuk valid dari ekspansi-brace, yang dalam hal ini mengharapkan bilangan bulat ... Ini hanya menjadi bentuk yang valid setelah ekspansi $ var, yang evalkemudian melewati proses yang sama untuk menghasilkan ekspansi urutan brace normal ... 1 2 3 QED;) ... Ringkasan: kehadiran sederhana dari pasangan brace tidak memicu ekspansi brace ... hanya bentuk yang valid yang memicu itu .
Peter.O
@ fred: Selamat datang :-)
asoundmove
Saat menggunakan variabel seperti a={0..9}; echo $a;tidak ada ekstensi brace . Menggunakan eval, itu berhasil. Jadi saya pikir penjelasan @ mattdm baik.
dr0i
20

Seperti yang sudah Anda sadari, {1..3}ekspansi ke 1 2 3tetapi {foo..bar}atau {$foo..$bar}tidak memicu ekspansi penjepit, dan yang terakhir kemudian diperluas untuk menggantikan $foodan $bardengan nilai-nilai mereka.

Sebuah fallback pada GNU (mis. Linux yang tidak tertanam) adalah seqperintahnya.

for x in `seq $ext0 $extN`; do rm foo.$x; done

Kemungkinan lain. jika foo.tidak mengandung karakter khusus shell, adalah

rm `seq $ext0 $extN | sed 's/^/foo./'`

Solusi paling sederhana adalah menggunakan zsh, di mana rm foo.{$ext0..$extN}melakukan apa yang Anda inginkan.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Terima kasih atas pilihannya. Senang melihat semuanya dikelompokkan .. Saya akan tetap menggunakan bash dan eval ... eval cukup sederhana sekarang karena saya tahu apa yang terjadi di balik layar; jadi itu bukan masalah lagi. Tidak perlu mengganti kuda yang baik karena pengendara
:)
1

Sementara jawaban lain membahas penggunaan evaldan seq, dalam bash, Anda dapat menggunakan forloop gaya C tradisional dalam konteks aritmatika. Variabel ext0dan extNdiperluas di dalam ((..))menyebabkan loop dijalankan untuk rentang yang ditentukan.

for (( idx = ext0; idx <= extN; idx++ )); do
    [[ -f "$foo.$idx" ]] || { printf "file %s does not exist" "$foo.$idx" >&2 ; continue }
    rm "$foo.$idx"
done

Jika Anda mencari cara yang optimal dan menghindari beberapa rmperintah, Anda dapat menggunakan placeholder sementara untuk menyimpan hasil nama file dan memanggil rmdalam satu tembakan.

 results=()
 for (( idx = ext0; idx <= extN; idx++ )); do
     [[ -f "$foo.$idx" ]] || { printf "file %s does not exist" "$foo.$idx" >&2 ; continue }
     results+=( "$foo.$idx" )
 done

dan sekarang panggil rmperintah pada array yang diperluas

 rm -- "${results[@]}"
Inian
sumber
0
    function f_over_range {
        for i in $(eval echo {$1..$2}); do
            f $i
        done
    }

    function f {
        echo $1
    }

    f_over_range 0 5
    f_over_range 00 05

Catatan:

  • Menggunakan eval memperlihatkan risiko keamanan injeksi perintah
  • Linux akan mencetak "00 \ n01 \ n02..etc", tetapi OSX akan mencetak "1 \ n2 \ n ... dll"
  • Menggunakan seq atau C-style untuk loop tidak akan cocok dengan penanganan angka nol di brace ekspansi
Siap
sumber