Mengemis perintah ekor menghasilkan output yang tidak terduga?

8

Perintah ini, ketika dijalankan sendiri, menghasilkan hasil yang diharapkan (baris terakhir dari crontab):

tail -n 1 /etc/crontab

Namun, ketika saya menjalankannya sebagai bagian dari perintah gema untuk mengirim hasil ke file, itu menambahkan ringkasan semua file di direktori kerja, ditambah hasil yang diharapkan:

sudo bash -c 'echo $(tail -n 1 /etc/crontab) > /path/to/file'

Mengapa perintah ini menghasilkan data tambahan?

Davidw
sumber
3
Apa yang kamu echolakukan di sini? Pertimbangkan jugatail -n 1 /etc/crontab | sudo tee /path/to/file >/dev/null
ctrl-alt-delor

Jawaban:

22

Garis crontab Anda memiliki satu atau lebih tanda bintang *di dalamnya, yang menunjukkan "kapan saja". Ketika baris itu diganti dari substitusi perintah, hasilnya adalah seperti

echo * * * * * cmd > /path/to/file

Sementara sebagian besar ekspansi lebih lanjut tidak diterapkan pada output substitusi perintah, ekspansi pathname adalah (seperti halnya pemisahan bidang) :

Hasil substitusi perintah tidak akan diproses untuk ekspansi tilde lebih lanjut, ekspansi parameter, substitusi perintah, atau ekspansi aritmatika. Jika substitusi perintah terjadi di dalam tanda kutip ganda, pemisahan bidang dan perluasan nama jalur tidak akan dilakukan pada hasil substitusi.

Perluasan Pathname adalah apa yang berubah *.txtmenjadi daftar nama file yang cocok (globbing), di mana *cocok dengan semuanya. Hasil akhirnya adalah Anda mendapatkan setiap nama file (tidak tersembunyi) di direktori kerja yang terdaftar untuk setiap *di baris crontab Anda.


Anda bisa memperbaikinya dengan mengutip ekspansi, jika kode yang Anda posting adalah perwakilan dari perintah yang lebih kompleks:

sudo bash -c 'echo "$(tail -n 1 /etc/crontab)" > /path/to/file'

tetapi yang lebih jelas adalah kehilangan echosemuanya:

sudo bash -c 'tail -n 1 /etc/crontab > /path/to/file'

Ini harus melakukan apa yang Anda inginkan dan juga lebih sederhana (satu-satunya perbedaan materi lainnya adalah bahwa versi ini akan menghilangkan pemisahan bidang yang jika tidak akan terjadi, sehingga run spasi tidak akan runtuh).

Michael Homer
sumber
5
Karena / etc / crontab hampir selalu dapat dibaca dunia, semua tipu daya "bash -c" sebenarnya tidak perlu: tail -n -1 /etc/crontab | sudo tee /path/to/fileidiom yang menurut saya paling tidak rentan kesalahan saat mengarahkan output ke file yang memerlukan hak superuser.
Bass
5

Mari kita pertimbangkan direktori dengan file-file ini:

$ ls
crontab  file1  file2  file3
$ cat crontab
f*

Sekarang, mari kita jalankan perintah tail:

$ tail -n 1 crontab
f*

Di atas adalah baris terakhir crontabdan inilah yang kami harapkan. Namun:

$ echo $(tail -n 1 crontab)
file1 file2 file3

Kutipan ganda menghilangkan masalah ini:

$ echo "$(tail -n 1 crontab)"
f*

Tanpa tanda kutip ganda, hasil substitusi perintah diperluas oleh shell. Salah satu ekspansi adalah ekspansi pathname . Dalam kasus di atas, ini berarti f*diperluas untuk mencocokkan setiap nama file yang dimulai dengan f.

Kecuali Anda secara eksplisit menginginkan ekspansi shell, masukkan semua variabel shell Anda dan / atau substitusi perintah di dalam tanda kutip ganda.

John1024
sumber
4

mecanism globing shell akan diperluas *ke file lokal.

garis crontab kemungkinan memiliki tempat *sebagai pengganti.

mis. baris ini dalam crontab dijalankan pada jam 7.47 pagi pada hari minggu, bintang pertama berarti setiap hari, kedua bulan apa saja.

47  7 * * 0 /run/on/sunday

maka Anda tail, dan masalah

echo 47  7 * * 0 /run/on/sunday

yang akan diperluas *ke file lokal.

Archemar
sumber