Bisakah saya menggunakan variabel dalam ekspansi Bash brace?

10

Di bawah ini adalah semacam pseudo-code untuk apa yang ingin saya capai:

#!/bin/bash

# I already have the variable below figured out (positive integer):
numlines=$([returns number of lines containing specific characters in a file])

# This is basically what I want to do with it:
for i in {1..$numlines}; do
    # the part below is already figured out as well:        
    do some other stuff
done

Saya bisa menjalankannya dengan baik dari baris perintah dengan memasukkan angka aktual dalam urutan `{1..n} '. Saya hanya perlu tahu apakah mungkin untuk memasukkan variabel di sini dan bagaimana cara melakukannya.

  • Saya telah mencoba exporting
  • Saya telah mencoba menempatkan variabel itu sendiri dalam kurung kurawal di dalam urutan: {1..${numlines}}
  • Saya telah mencoba memasukkannya ke dalam tanda kutip ganda berharap itu akan berkembang: {1.."$numlines"}
  • Saya telah mencoba melarikan diri dari $:{1..\$numlines}

Apakah saya perlu menggunakan set -[something]perintah agar variabel ini bisa diperluas? Saya bahkan telah mencoba beberapa bentuk penggunaan eval... semuanya sia-sia.

Saya hanya perlu tahu apakah ada sesuatu yang sederhana atau tidak jelas yang saya lewatkan atau apakah ini bahkan mungkin sebelum saya buang waktu lagi.

Saya bisa menggabungkan cara yang benar-benar sangat hack untuk membuatnya berfungsi sesuai kebutuhan, tetapi saya ingin menghindari itu jika memungkinkan dan belajar cara yang benar untuk melakukannya.

rubynorails
sumber

Jawaban:

11

Sayangnya, tidak ada cara untuk menggunakan variabel dalam ekspansi itu (AFAIK), karena ekspansi variabel terjadi setelah ekspansi penjepit.

Untungnya, ada alat yang melakukan pekerjaan yang sama.

for i in $(seq 1 $numlines); do
    # stuff
done

seqberasal dari GNU coreutils; tidak tahu bagaimana melakukannya di POSIX.

Tom Hunt
sumber
1
(Plus 1). seqbagus untuk sistem GNU dan, jika saya ingat dengan benar, OSX terbaru. Pada sistem BSD lainnya, seseorang dapat menggunakan jot sebagai gantinya.
John1024
seqbekerja dengan sempurna. Terima kasih banyak atas jawaban yang cepat.
rubynorails
Ini bukan bagian dari pertanyaan saya - tetapi apa sintaks (jika ada) untuk melakukan ini dalam urutan terbalik, seperti {16..1}? $(seq $numlines 1)tidak bekerja. Saya kira saya selalu bisa man seq, tetapi hanya ingin tahu apakah ada yang tahu dari kepala mereka.
rubynorails
1
Baru tahu cara melakukannya secara terbalik dari tautan ini -for i in $(seq $numlines -1 1)
rubynorails
seq ${numlines} -1 0
DopeGhoti
10

Tentu. Jika Anda menginginkan for for yang menambah variabel integer, gunakan bentuk forloop yang menambah variabel integer (atau lebih umum melakukan aritmatika pada variabel loop).

for ((i=1; i<=numlines; i++)); do  done

Konstruk ini berfungsi di bash (dan ksh93 dan zsh), tetapi tidak di sh polos. Dalam sh polos, gunakan loop sementara dan [ … ]konstruksi test ( ).

i=1
while [ "$i" -le "$numlines" ]; do
  
  i=$((i+1))
done
Gilles 'SANGAT berhenti menjadi jahat'
sumber
8

Jika Anda harus menghindari seq, yang seperti ditunjukkan Tom Hunt tampaknya menjadi solusi yang biasa untuk ini, maka evalpasti bisa melakukannya (meskipun, saya tidak akan mendorongnya):

eval 'for i in {1..'$numlines'}; do echo $i; done'

Anda dapat tetap POSIX dengan menghindari ekspansi {}, dan cukup melakukan matematika dan perbandingan integer pada $numlines:

while [ ! "$numlines" -eq 0 ]; do
     echo "$numlines"
     : $((numlines-=1))
done

Di luar POSIX, bashdan kshdan zshjuga memiliki forloop gaya-C :

for((i=0; i<numlines; i++)); do echo $i; done
PSkocik
sumber
1
Saya sangat menghargai jawaban ini juga. Walaupun seqbekerja dengan baik untuk skenario saya dan tampaknya menjadi solusi paling sederhana, baik untuk mengetahui bahwa ada alternatif lain (bahkan POSIX). Terima kasih untuk ini.
rubynorails
2
Tidak ada alasan untuk menggunakannya eval; jika Anda memiliki ekspansi brace, Anda memiliki loop gaya-C.
chepner
@PSkocik - Jika saya bisa memilih 2 jawaban, saya juga akan memilih yang ini. Ketika saya menemukan fakta bahwa saya perlu melakukan ini secara terbalik, evalcontoh Anda adalah yang paling sederhana dan akan menyelamatkan saya dari keharusan mencari cara alternatif untuk menggunakannya seq. The whileLoop adalah besar sedikit bagi saya. Saya suka menjaga hal-hal singkat dan manis, dan saya tidak pernah bisa mendapatkan forloop gaya-C untuk bekerja dengan memberikan inilai 0 atau 1. Tidak pernah kembali dengan benar dan selalu sedikit tidak aktif. Saya yakin itu bisa di-tweak untuk bekerja dengan benar, tetapi ini jelas merupakan solusi yang membantu, toh.
rubynorails
The evalPendekatan ini bermasalah jika ada sesuatu yang non-sepele dalam tubuh loop. Saya membayangkan itu tidak akan terlalu mudah dibaca jika Anda perlu membuat dua loop seperti itu.
kasperd