Perintah Bash dalam string dijalankan ketika saya membuat string, bukan ketika saya menggunakannya nanti

10

Saya relatif baru untuk shell scripting, tetapi hampir menyelesaikan skrip yang menggunakan program lftp . Bagian dari skrip yang saya mengalami masalah adalah ketika saya membuat string panjang perintah (dipisahkan oleh ;).

for var in something
do
    ...
    commands_to_run+="echo Result is `tail -n 1 $somefile`;"
done

Apa yang saya perhatikan adalah bahwa tailprogram - dibungkus dengan backticks - sedang dijalankan ketika for loop berulang, tetapi tidak ketika saya memanggil string perintah nanti dalam skrip saya.

Sayangnya, isi $ somefile belum siap untuk diperiksa. Bagaimana saya bisa mendapatkan perintah untuk dieksekusi ketika saya membutuhkannya, dan tidak saat saya membuat string?

Ricky
sumber

Jawaban:

8

Yang ini agak rumit. Informasi yang Hauke ​​berikan benar, itu hanya masalah menguraikannya untuk kasus penggunaan Anda.

Cara termudah adalah dengan menggunakan $()sintaks sambil melarikan diri $sehingga definisi variabel tidak menjalankan perintah yang dilampirkan oleh $()pada saat definisi. Peringatannya adalah bahwa hasil akhirnya harus dievaluasi kembali (via eval) oleh shell pada saat eksekusi aktual untuk dieksekusi perintah bersarang.

Jauh lebih mudah untuk melihat contoh, jadi ambil yang ini, yang seharusnya menempatkan Anda di jalur yang benar:

for((i=0;i<10;i++)); do 
  date +%s.%N  # Print a timestamp (in format seconds.nanoseconds)
  test="echo \$(date +%s.%N)" # Save a command to do the same
  sleep 1      # Sleep for a second
  eval "$test" # Evaluate the command saved in variable 'test'
  echo         # Print a new line before the next iteration
done

Berikut contoh output dari contoh di atas (dipangkas menjadi satu iterasi):

1398832186.133661344
1398832187.139076728

Anda akan melihat bahwa cap waktu kedua untuk setiap loop adalah sekitar satu detik setelah yang pertama. Sebaliknya, jika Anda melakukan tes yang sama tanpa lolos $dari testdefinisi dan menghapusnya eval, kedua cap waktu akan hampir cocok.

Jangan terbiasa menggunakannya evaldalam kebanyakan situasi, tetapi ini adalah salah satu di mana saya tidak tahu cara yang baik untuk menghindarinya. Semoga ini bisa membantu. Semoga berhasil!

daBeamer
sumber
Terima kasih banyak, saya memang mencoba menggunakan $(...)seperti yang disarankan Hauke ​​tetapi backslash adalah kuncinya.
Ricky
Senang itu membantu - ingat, kuncinya di sini adalah evalkarena Anda dapat melakukan hal yang sama dengan tidak melarikan diri $dan menggunakan tanda kutip tunggal ( ') daripada tanda kutip ganda ( ") untuk mengelilingi perintah Anda.
daBeamer
Sekarang saya baru sadar, sama seperti saran Huake, begitu saya mencoba menggunakan ini di program lftp gema hanya mencetak perintah, itu tidak akan benar-benar menjalankannya. Mungkin harus mencoba milis mereka untuk bantuan yang lebih spesifik.
Ricky
Perintah apa yang sedang Anda coba jalankan? Saya mendapat kesan Anda ingin echostring dengan konten termasuk output dari perintah bersarang eksekusi tertunda.
DaBeamer
1
@ Rick saya harus setuju dengan semua poin dari @ HaukeLaging. Kode as-is minus the echotidak akan bekerja karena tidak ada perintah eval, tetapi sebuah string. Jika Anda memiliki contoh yang lebih relevan bagi kami, kami dapat mencoba membantu.
daBeamer
6

Ada beberapa tingkatan kutipan. Tanda kutip ganda ( "...") spasi melindungi dan beberapa karakter khusus ( ~, &, |, ;, ...) tetapi tidak mencegah perluasan parameter dan substitusi perintah.

Anda memerlukan tanda kutip tunggal ( ') atau garis miring terbalik di depan karakter "berbahaya".

Secara umum: Anda harus mempertimbangkan untuk menggunakan $(tail ...)backticks. Backticks adalah standar yang lebih tua tetapi kita berbicara tentang yang sudah terlalu tua sehingga $()tidak menimbulkan masalah bagi kebanyakan orang. Versi baru lebih mudah dibaca dan dapat disarangkan. Jangankan masalah pemformatan di sini di sx ...

Hauke ​​Laging
sumber
Terima kasih atas jawaban cepat Hauke. Sayangnya mengganti backticks dengan yang direkomendasikan $(...)masih menghasilkan hasil yang sama - shell mengeksekusi ini ketika string saya didefinisikan.
Ricky
@ Ricky Itu bukan saran alternatif. Anda harus menggunakan $()tetapi Anda tetap membutuhkan tanda kutip tunggal.
Hauke ​​Laging
Jadi tidak ada kombinasi karakter yang akan mencapai apa yang saya cari?
Ricky
@Ricky Apa yang sangat sulit untuk dipahami tentang "Anda perlu salah satu kutipan" Anda jelas tidak mencobanya.
Hauke ​​Laging
Sebenarnya saya lakukan, tetapi ketika saya menggunakan tanda kutip tunggal gema hanya akan mencetak semuanya pada baris itu sebagai string normal. Apa yang sulit dari mungkin memberikan contoh?
Ricky