Saya memiliki biner (yang tidak dapat saya modifikasi) dan saya dapat melakukannya:
./binary < file
Saya juga bisa melakukan:
./binary << EOF
> "line 1 of file"
> "line 2 of file"
...
> "last line of file"
> EOF
Tapi
cat file | ./binary
memberi saya kesalahan. Saya tidak tahu mengapa itu tidak bekerja dengan pipa. Dalam semua 3 kasus, konten file diberikan ke input standar biner (dalam berbagai cara):
- bash membaca file dan memberikannya ke stdin dari biner
- bash membaca baris dari stdin (hingga EOF) dan memberikannya ke stdin dari biner
- cat membaca dan meletakkan baris file ke stdout, bash mengarahkan mereka ke stdin biner
Biner seharusnya tidak memperhatikan perbedaan antara 3 sejauh yang saya mengerti. Adakah yang bisa menjelaskan mengapa case ke-3 tidak berfungsi?
BTW: Kesalahan yang diberikan oleh biner adalah:
20170116 / 125624.689 - U3000011 Tidak dapat membaca file skrip '', kode kesalahan '14'.
Tapi pertanyaan utama saya adalah, bagaimana ada perbedaan untuk setiap program dengan 3 opsi itu.
Berikut adalah beberapa perincian lebih lanjut: Saya mencobanya lagi dengan strace dan ternyata ada beberapa kesalahan ESPIPE (pencarian ilegal) dari lseek diikuti oleh EFAULT (Alamat buruk) dari pembacaan tepat sebelum pesan kesalahan.
Biner yang saya coba kontrol dengan skrip ruby (tanpa menggunakan file sementara) adalah bagian dari callapi dari Automic (UC4) .
sumber
isatty()
mengembalikan false untuk akan menjadi file yang dapat dicari atau mmappable ...cat
pencegah di dalamnya. Tampaknya Anda tidak dapat menggunakannya untuk menggabungkan dua file, seperti penggunaan yang dimaksudkan.Jawaban:
Di
binary
stdin adalah file yang dibuka dalam mode read-only. Catatan yangbash
tidak membaca file sama sekali, itu hanya membukanya untuk membaca file descriptor 0 (stdin) dari proses yang dijalankannyabinary
.Di:
Bergantung pada shell,
binary
stdin akan berupa file sementara yang dihapus (AT&T ksh, zsh, bash ...) yang berisitest\n
seperti yang diletakkan di sana oleh shell atau ujung pembacaan pipa (dash
,yash
; dan shell menulistest\n
secara paralel) di ujung lain pipa). Dalam kasus Anda, jika Anda menggunakanbash
, itu akan menjadi file temp.Di:
Tergantung pada cangkang,
binary
stdin akan menjadi ujung pembacaan pipa, atau salah satu ujung pasangan soket di mana arah penulisan telah ditutup (ksh93) dancat
sedang menulis kontenfile
di ujung lainnya.Ketika stdin adalah file biasa (sementara atau tidak), itu dapat dicari.
binary
dapat pergi ke awal atau akhir, mundur, dll. Ini juga dapat mmap itu, melakukan beberapaioctl()s
seperti FIEMAP / FIBMAP (jika menggunakan<>
bukan<
, itu bisa memotong / membuat lubang di dalamnya, dll).pasangan pipa dan soket di sisi lain adalah sarana komunikasi antar-proses, tidak banyak yang
binary
bisa dilakukan selainread
data (meskipun ada juga beberapa operasi seperti beberapa pipa-spesifikioctl()
yang bisa dilakukan pada mereka dan tidak pada file biasa) .Sebagian besar waktu, itu kemampuan yang hilang untuk
seek
yang menyebabkan aplikasi gagal / mengeluh ketika bekerja dengan pipa, tetapi bisa menjadi salah satu panggilan sistem lain yang berlaku pada file biasa namun tidak pada berbagai jenis file (sepertimmap()
,ftruncate()
,fallocate()
) . Di Linux, ada juga perbedaan besar dalam perilaku ketika Anda membuka/dev/stdin
ketika fd 0 berada di atas pipa atau pada file biasa.Ada banyak perintah di luar sana yang hanya dapat menangani file yang dapat dicari , tetapi ketika itu masalahnya, itu umumnya tidak untuk file yang terbuka di stdin mereka.
unzip
perlu membaca indeks yang disimpan di akhir file, dan kemudian mencari dalam file untuk membaca anggota arsip. Tapi di sini, file (reguler dalam kasus pertama, pipa dalam yang kedua) diberikan sebagai argumen pathunzip
, danunzip
membukanya sendiri (biasanya pada fd selain 0) alih-alih mewarisi fd yang sudah dibuka oleh induknya. Itu tidak membaca file zip dari stdin-nya. stdin banyak digunakan untuk interaksi pengguna.Jika Anda menjalankan itu
binary
dari Anda tanpa pengalihan pada prompt shell interaktif yang berjalan di emulator terminal, makabinary
stdin akan diwarisi dari induknya shell, yang dengan sendirinya akan mewarisinya dari induknya terminal emulator dan akan menjadi Perangkat pty terbuka dalam mode baca + tulis (sesuatu seperti/dev/pts/n
).Perangkat-perangkat itu juga tidak bisa dicari. Jadi, jika
binary
berfungsi dengan baik ketika mengambil input dari terminal, mungkin masalahnya bukan tentang mencari.Jika 14 itu dimaksudkan sebagai errno (kode kesalahan yang ditetapkan oleh kegagalan panggilan sistem), maka pada sebagian besar sistem, itu akan menjadi
EFAULT
( Alamat buruk ). Theread()
system call akan gagal dengan kesalahan bahwa jika diminta untuk membaca ke alamat memori yang tidak dapat ditulis. Itu akan terlepas dari apakah fd membaca data dari titik ke pipa atau file biasa dan umumnya akan menunjukkan bug 1 .binary
mungkin menentukan jenis file yang terbuka pada stdin (withfstat()
) dan mengalami bug ketika itu bukan file biasa atau perangkat tty.Sulit dikatakan tanpa mengetahui lebih lanjut tentang aplikasi tersebut. Menjalankannya di bawah
strace
(atautruss
/tusc
setara pada sistem Anda) dapat membantu kami melihat apa yang disebut sistem panggilan jika ada yang gagal di sini.1 Skenario yang dibayangkan oleh Matthew Ife dalam komentar untuk pertanyaan Anda terdengar sangat masuk akal di sini. Mengutipnya:
sumber
./binary < file
dapat dicari!open
dan berperilaku sama dengan file yang telahopen
diedit. Itu kebetulan telah diwarisi dari proses induk, tapi itu tidak biasa.open("/proc/self/fd/0", O_RDWR)
bekerja, bahkan pada file yang dihapus. Konyol saya: P.echo foo>foo; (sleep 0.5; ll -L /proc/self/fd/0; strace ./a.out; ll -L /proc/self/fd/0) < foo & sleep 0.1 && rm foo
batalkan tautanfoo
sebelum a.out berjalan dengan stdin yang dialihkan darifoo
.Berikut adalah contoh program sederhana yang menggambarkan jawaban Stéphane Chazelas menggunakan
lseek(2)
inputnya:Pengujian:
Pipa tidak bisa dicari, dan itu adalah satu tempat di mana sebuah program mungkin mengeluh tentang pipa.
sumber
Pipa dan pengalihan adalah hewan yang berbeda, jadi untuk berbicara. Ketika Anda menggunakan
here-doc
pengalihan (<<
) atau mengarahkan stdin<
teks tidak keluar dari udara tipis - itu benar-benar masuk ke file descriptor (atau file sementara, jika Anda mau), dan di situlah biner's stdin akan menunjuk.Secara khusus, inilah kutipan dari
bash's
kode sumber, file redir.c (versi 4.3):Jadi karena pengalihan pada dasarnya dapat diperlakukan sebagai file, binari dapat menavigasi mereka, atau
seek()
melalui file dengan mudah, melompat ke byte file apa pun.Pipes, karena mereka adalah buffer 64 KiB (setidaknya di Linux) dengan penulisan 4096 byte atau kurang dijamin atom, tidak dapat dicari, artinya Anda tidak dapat menavigasi dengan bebas - hanya membaca secara berurutan. Saya pernah mengimplementasikan
tail
perintah dengan python. 29 juta baris teks dapat dicari dalam mikrodetik jika diarahkan, tetapi jikacat
melalui pipa, tidak ada yang bisa dilakukan - jadi semuanya harus dibaca berurutan.Kemungkinan lain adalah bahwa biner mungkin ingin membuka file secara khusus, dan tidak ingin menerima input dari sebuah pipa. Biasanya dilakukan melalui
fstat()
system call, dan memeriksa apakah input berasal dariS_ISFIFO
jenis file (yang menandakan pipa / pipa bernama).Biner khusus Anda, karena kami tidak tahu apa itu, mungkin berusaha mencari, tetapi tidak dapat mencari pipa. Anda disarankan untuk membaca dokumentasinya untuk mengetahui apa sebenarnya arti kode kesalahan 14.
CATATAN : Beberapa shell, seperti dash (Debian Almquist Shell, default
/bin/sh
di Ubuntu) menerapkanhere-doc
redirection dengan pipa secara internal , sehingga mungkin tidak dapat dicari. Intinya tetap sama - pipa berurutan dan tidak dapat dinavigasi dengan mudah, dan upaya untuk melakukannya akan menghasilkan kesalahan.sumber
dash
melakukannya. Jawaban ini menjelaskan perilaku yang diamati dengan bash, tetapi perilaku itu tampaknya tidak dijamin di shell lain.dash
di sistem saya. Saya tidak menyadarinya sebelumnya. Terima kasih telah menunjukkanfstat()
stdin untuk memeriksa apakah itu sebuah pipa.stat
mengambil pathname. Tapi sungguh, hanya mencobalseek
adalah mungkin cara yang paling waras untuk menentukan apakah suatu fd dapat dicari setelah itu sudah terbuka.Perbedaan utama adalah dalam penanganan kesalahan.
Dalam kasus berikut kesalahan dilaporkan
Dalam kasus berikut kesalahan tidak dilaporkan.
Dengan bash, Anda masih dapat menggunakan PIPESTATUS:
Tetapi ini hanya tersedia segera setelah eksekusi perintah:
Ada perbedaan lain, saat kita menggunakan fungsi shell alih-alih binari. Dalam
bash
, fungsi-fungsi yang merupakan bagian dari pipeline dieksekusi dalam sub-shells (kecuali untuk komponen pipeline terakhir jikalastpipe
opsi diaktifkan danbash
non-interaktif), sehingga perubahan variabel tidak memiliki efek dalam shell induk:sumber
>
dilakukan oleh shell, tetapi dengan pipa itu dilakukan oleh perintah yang menghasilkan teks. BAIK. Tetapi dalam pertanyaan khusus ini, OP menggunakan file yang sudah ada, jadi itu bukan masalah, dan jelas kesalahan yang dihasilkan oleh biner.