Mengapa urutan penting dalam pelaksanaan perintah bash ini?

10

Tampaknya ada beberapa ketidakkonsistenan yang tidak dapat saya mengerti tentang shell bash.

Jika saya menjalankan:

ls;date;time

hasil dari tiga pertanyaan ditampilkan secara berurutan.

Namun, pada posisi tanggal dan waktu yang dipertukarkan, pesan kesalahan muncul.

Jadi jika saya mengeksekusi:

ls;time;date

pesan kesalahan mengatakan: bash: syntax error near unexpected token 'date'.

Adakah yang bisa menjelaskan hal ini?

rohitvijaysharma
sumber
Masalah kebohongan Anda pada time;datevs date;time. Ini tampaknya menjadi masalah dengan pipeline in bashdan char terakhir yang dihasilkan dengan timeoutput. Hasil yang diuji di emulator terminal berbeda adalah: - [Bash] $ date; time # [OK] $ time; date # [ NotOK ] bash: kesalahan sintaks dekat token tak terduga `date '$ error hanya kesalahan # waktu saja tidak muncul bahwa itu adalah hasil dari tanggal apa pun. - [Csh] $ date; waktu # [OK] $ waktu; tanggal # [OK] - [Tcsh] $ date; waktu # [OK] $ waktu; tanggal # [OK] - [Ksh] $ tanggal; waktu # [ OK] $ waktu; tanggal # [OK]
Mostafa Shahverdy
Saya telah memperbarui jawaban saya dengan penjelasan untuk pesan kesalahan. Harap periksa bahwa ini adalah jawaban yang Anda cari.
zwets

Jawaban:

10

The timeperintah dalam pipa Anda bukan /usr/bin/timebiner, tetapi bash timebuilt-in. Bandingkan man timedengan help time. Kesalahan yang Anda lihat adalah bash gagal menguraikan timeargumen. Ini harus ada atau menjadi baris baru. Ini adalah baris baru dalam contoh pertama Anda tetapi tidak ada di baris kedua.

Di sisi lain, jika Anda lari

ls;date;'time'

atau

ls;'time';date

di mana kutipan sekitar 'time'mencabut statusnya sebagai kata yang dilindungi undang-undang, maka bash tidak memiliki masalah untuk menguraikan baris. Sekarang mem-parsing tiga perintah dalam daftar, yang akan dieksekusi secara berurutan, dan /usr/bin/timeakan melaporkan kesalahan penggunaan dalam kedua kasus.

Tambahan

Diamati bahwa meskipun time ; datemenghasilkan kesalahan, time ; ; datetidak. Penjelasan yang mungkin adalah bahwa time ;ditafsirkan oleh bash setara dengan time <newline>. Ekspresi time ; ; dateini kemudian diuraikan sebagai daftar time ;dan date.

Ini konsisten dengan pengamatan yang time ;dan time ; ;juga legal, yang kedua diuraikan sebagai daftar tunggal yang time ;diikuti oleh tanda titik koma yang diizinkan setelah daftar.

Jadi cara lain untuk menjelaskan mengapa time ; datemenghasilkan kesalahan bash: syntax error near unexpected token 'date'adalah bahwa timemengkonsumsi titik koma yang memisahkannya date. Itu hanya dapat melakukan itu karena timeini adalah kata yang dilindungi undang-undang.

zwets
sumber
Terima kasih atas penjelasannya! Tapi sekali lagi perilaku ini terlihat seperti bug bagi saya: timeseharusnya mengizinkan perintah NULL, dan tanda titik koma membatasi daftar, jadi IMO timeperintah tidak boleh "mengkonsumsi" tanda titik koma setelahnya. Perintah bawaan lainnya (yang dapat mengambil argumen) tidak menunjukkan perilaku semacam ini.
atur
@ mengatur Komplikasinya adalah bahwa waktu tidak memungkinkan perintah nol (yang akan membuat semuanya ambigu), itu hanya memungkinkan baris baru menggantikan perintah. Jadi time;datememang salah secara sintaksis dalam penafsiran apa pun. Namun time ; dan time ; ;juga ilegal. Hal ini dapat diperdebatkan apakah timeperilaku 's adalah bug atau hanya tidak tercatat (itu adalah internal konsisten), namun laporan bug pasti akan berada di tempat. Apakah Anda bersedia untuk mengajukannya?
zwets
Yah, saya melihat sumbernya (bash4.2: parse.y: lines 1205-1221) dan di sana tertulis itu time by itself can time a null commanddan kemudian ia melakukannya $$ = make_simple_command (x, (COMMAND *)NULL);. Adapun pengajuan bug saya tidak yakin 8)
mengatur
Harus dicatat bahwa masalah ini khusus untuk bash. Melakukan time ; datepekerjaan ksh93dan mkshtanpa kesalahan, meskipun ksh ada timekata kunci.
Sergiy Kolodyazhnyy
2

Bash memperlakukan built-in timesebagai kasus khusus, saat mem-parsing baris perintah.

Seperti yang dapat dibaca di bash manpage, baris yang diketik pertama-tama dibagi menjadi daftar:

pipeline ; pipeline

di mana sebuah pipa adalah:

[time [-p]] [ ! ] command [ [|⎪|&] command2 ... ]

atau dalam kasus kami, cukup:

time command

yaitu jika waktu hadir, maka perintah harus juga hadir.

[Ada kasus khusus yang memungkinkan timeuntuk diikuti oleh baris baru, tetapi itu tidak berlaku di sini]

Jadi, dalam kasus kami, kami memiliki:

time;date

dipecah menjadi dua jalur pipa:

1. time
2. date

dan pipa 1 tidak terbentuk dengan baik, karena kita punya timetanpa perintah. Karena itu kesalahannya.

Perhatikan bahwa baris perintah timejuga tidak berfungsi di sini:

$ /usr/bin/time;date
Usage: /usr/bin/time [-apvV] [-f format] [-o file] [--append] [--verbose]

bash mem-parsing ini seperti yang diharapkan, ke dalam 2 jalur pipa:

1. /usr/bin/time
2. date

dan /usr/bin/timekemudian menolak untuk berjalan tanpa argumen. Perhatikan bahwa ini adalah kesalahan /usr/bin/timebukan kesalahan dari bash.

Alasan bahwa back-tick berfungsi adalah bahwa back-tick berhenti timeditafsirkan sebagai elemen khusus dalam pipa.

yaitu dengan back-tick:

`time`;date

itu diurai sebagai dua pipa:

1. `time`
2. date

Ingatlah bahwa saluran pipa, dalam kasus kami, adalah:

[time] command

dan masalahnya pada awalnya adalah bahwa kami memiliki timetanpa perintah, yang tidak diizinkan. Tetapi sekarang kita hanya memiliki perintah:

`time`

tanpa sebelumnya time, karena back-ticks berarti timeditafsirkan sebagai perintah, bukan sebagai kata sebelumnya.

Jadi bash kemudian menjalankan builtin-nya timetanpa argumen, yang diterima. Tidak menghasilkan output, dan kami tidak melihat kesalahan.

Perhatikan bahwa:

`time`

sebenarnya menjalankan hasil dari timebuilt-in, yaitu menjalankan apa pun yang timedihasilkan built-in pada stdout. Tetapi karena timedengan sendirinya tidak menulis apa pun untuk stdout, tampaknya bekerja.

Akhirnya, telah dicatat bahwa ini bekerja:

time ; ; date

yang saya tidak bisa jelaskan, sayangnya :)

cdmackay
sumber
Saya pikir penjelasan Anda lebih baik, tetapi masih terlihat aneh bagi saya. ;datememberi bash: syntax error near unexpected token ;, tetapi time ;datememberi bash: syntax error near unexpected token date, jadi sepertinya bash tidak memperlakukan perintah setelah waktu yang dibangun sebagai "; date". Menariknya, time ; ; datebekerja.
atur
ya, terima kasih @ mengatur, ini cukup aneh. Saya akan memperbarui sedikit jawabannya.
cdmackay
ok, atur, sudah ditulis ulang. Masih belum bisa menjelaskan yang terakhir ... hah.
cdmackay
@cdmackay Anda mencampuradukkan backticks dan penawaran. Dengan mengutip kata 'time' itu kehilangan artinya sebagai kata yang dilindungi undang-undang. Backticking membuatnya dieksekusi dalam subkulit yang outputnya akan disambungkan ke perintah. Ini tidak ada hubungannya dengan diskusi. Faktanya, contoh Anda `time\';datemembuktikan kebalikan dari klaim Anda: ini harus memberikan kesalahan dengan alasan Anda karena /usr/bin/timememerlukan argumen. Alasannya tidak adalah karena dalam subshell di mana ia mengeksekusi itu adalah kata yang dipesan timesekali lagi.
zwets
@ mengatur Keduanya adalah kesalahan sintaksis dan keduanya dilaporkan berada di dekat tempat yang sama, jadi saya tidak melihat ada inkonsistensi di sana. Setelah memasuki bidang kesalahan sintaks, Anda tidak bisa mengharapkan parser tahu jalan keluarnya. Jika Anda memerlukan parser, maka itu harus tahu tidak hanya sintaks hukum, tetapi juga sintaksis dari setiap konstruk ilegal yang mungkin, yang tidak mungkin secara definisi.
zwets