Mengapa `sort <(ls-l)` bekerja tetapi `sort <(ls-l)` gagal?

32

Hari ini saya belajar sesuatu tentang fifo dengan artikel ini: Pengantar Named Pipes , yang menyebutkan cat <(ls -l).

Saya melakukan beberapa percobaan dengan menggunakan sort < (ls -l), yang muncul kesalahan:

-bash: syntax error near unexpected token `('`

Kemudian saya menemukan saya salah menambahkan ruang ekstra dalam perintah.

Tapi, mengapa perintah tambahan ini akan menyebabkan kegagalan ini? Mengapa simbol pengalihan harus dekat dengan (?

Zen
sumber
Perlu dicatat bahwa shell * nix membagi semuanya berdasarkan spasi putih yang menciptakan token yang disebutkan oleh Alec.
anak ayam

Jawaban:

45

Karena itu bukan <, itu <()yang sangat berbeda. Ini disebut proses substitusi , ini adalah fitur dari cangkang tertentu yang memungkinkan Anda untuk menggunakan output dari satu proses sebagai input untuk yang lain.

The >dan <operator redirect output ke dan masukan dari file . The <()Operator berkaitan dengan perintah (proses), bukan file. Ketika Anda berlari

sort < (ls)

Anda mencoba menjalankan perintah lsdalam sebuah subkulit (itulah arti tanda kurung), lalu meneruskan subkulit tersebut sebagai file input sort. Namun, ini tidak menerima sintaksis dan Anda mendapatkan kesalahan yang Anda lihat.

terdon
sumber
3
Jawaban Anda baik, tetapi then sort is attempting to read the subshell as its input file→ ini jelas salah, karena Bash bahkan tidak akan menguraikan sintaks. Baik lstidak sortbenar-benar dijalankan.
sleblanc
1
@sebleblanc fair point, ulangi jawabannya, terima kasih.
terdon
1
Tidak ada sub-shell dalam kasus ini. < (ls)bukan token yang valid di sini.
cuonglm
@cuonglm no, karena bash memperlakukannya sebagai kesalahan sintaksis. Maksud saya adalah bahwa (ls)akan berjalan lsdalam subkulit.
terdon
22

Karena memang begitulah seharusnya.

<(...)in bashadalah sintaks untuk substitusi proses. Ini disalin dari operator yang sama di ksh.

<, (, ), |, &, ;Adalah token leksikal khusus di bashyang digunakan untuk membentuk operator khusus dalam kombinasi yang berbeda. <, <(, <<, <&... masing-masing memiliki peran mereka. <adalah untuk pengalihan. <file, < fileakan mengarahkan input dari file. <'(file)'akan mengarahkan input dari file yang dipanggil (file), tetapi <(file)merupakan operator berbeda yang bukan merupakan operator redirection.

< (file)akan <diikuti oleh (file). Dalam konteks itu, dalam bash, (file)tidak valid. (...)dapat valid sebagai token tunggal dalam beberapa konteks seperti:

(sub shell)
func () {
  ...
}
var=(foo bar)

Tapi tidak di

sort < (cmd)

Dalam fishshell, ini berbeda. In fish, (...)adalah untuk substitusi perintah (setara dengan $(...)in bash). Dan <untuk pengalihan input seperti di shell Bourne-like.

Jadi di fish:

sort <(echo file)

akan sama dengan:

sort < (echo file)

Itu adalah:

sort < file

Tapi itu sesuatu yang sama sekali berbeda dari bashproses substitusi.

Dalam yashshell, shell POSIX lain, <(...)bukan untuk substitusi proses tetapi untuk pengalihan proses

Di sana,

sort <(ls -l)

Pendek untuk:

sort 0<(ls -l)

adalah operator pengalihan. Ini kurang lebih setara dengan:

ls -l | sort

Sementara di bash, <(ls -l)diperluas ke jalur pipa, jadi itu lebih seperti:

ls -l | sort /dev/fd/0

Dalam zsh, (...)kelebihan beban sebagai operator globbing ( (*.txt|*.png)akan diperluas ke txtdan pngfile) dan sebagai kualifikasi glob ( *(/)misalnya diperluas ke file direktori).

Di zsh, di:

sort < (ls -l)

Itu (ls -l)akan diperlakukan sebagai kualifikasi glob. The lgumpal kualifikasi adalah untuk mencocokkan pada jumlah link dan mengharapkan angka setelah l(seperti dalam ls -ld ./*(l2)akan daftar file dengan 2 link), sehingga ini mengapa Anda mendapatkan zsh: number expectedkesalahan di sana.

sort < (w)akan memberikan zsh: no matches found: (w)kesalahan sebagai gantinya (w)cocok dengan file dengan nama kosong yang dapat ditulisi.

sort < (w|cat)akan mengurutkan konten wdan / atau catfile di direktori saat ini ...

Stéphane Chazelas
sumber
mengapa sort < $(ls -l)memberikan kesalahan ini:bash: $(ls -l): ambiguous redirect
Edward Torvalds
@edwardtorvalds, karena $(ls -l)diperluas hingga lebih dari satu kata. Gunakan tanda kutip untuk mencegah split + glob ( sort < "$(echo file)"). Perhatikan bahwa perilaku atau bashberbeda dari POSIX sh dalam bash apakah itu membagi + glob di sana juga ketika non-interaktif (bukan ketika dipanggil seolah- sholah).
Stéphane Chazelas
dengan melihat ls -l | sort /dev/fd/0saya dapat mengatakan bahwa output ls -ldisimpan /dev/fd/0dan sortperintah membacanya untuk memberikan output yang diinginkan. Saya menggunakan tail -f --retry /dev/fd/0untuk memonitor file itu tetapi saya tidak mendapatkan output apa pun. Mengapa? bagaimana saya bisa membaca file itu?
Edward Torvalds
Pada ikan, Anda dapat menggunakan (foo | psub)untuk mencapai substitusi proses input; belum ada pengganti (ha) untuk substitusi proses keluaran.
Zanchey