У меня есть двоичный файл (который я не могу изменить), и я могу сделать:
./binary < file
Я также могу сделать:
./binary << EOF
> "line 1 of file"
> "line 2 of file"
...
> "last line of file"
> EOF
Но
cat file | ./binary
дает мне ошибку. Я не знаю, почему это не работает с трубой. Во всех трех случаях содержимое файла передается на стандартный ввод двоичного файла (по-разному):
- bash читает файл и передает его в стандартный двоичный файл
- bash читает строки из стандартного ввода (до EOF) и передает его в стандартный двоичный файл
- cat читает и помещает строки файла в стандартный вывод, bash перенаправляет их в стандартный двоичный файл
Двоичный файл не должен заметить разницу между этими 3, насколько я понял. Может кто-нибудь объяснить, почему третий случай не работает?
Кстати: ошибка, заданная двоичным файлом :
20170116 / 125624.689 - U3000011 Не удалось прочитать файл сценария '', код ошибки '14'.
Но мой главный вопрос: какова разница для любой программы с этими 3 вариантами?
Вот некоторые подробности: я попробовал это снова с помощью strace, и на самом деле были некоторые ошибки ESPIPE (незаконный поиск) от lseek, за которыми следовал EFAULT ( неправильный адрес) при чтении прямо перед сообщением об ошибке.
Двоичный файл, который я пытался контролировать с помощью сценария ruby (без использования временных файлов), является частью программы Callapi из Automic (UC4) .
источник
isatty()
возвращает ложь, будет доступным для поиска или доступным файлом ...cat
превентор. Похоже, что вы не можете использовать его для объединения двух файлов, как это предполагается.Ответы:
В
binary
sdin - это файл, открытый в режиме только для чтения. Обратите внимание, чтоbash
файл вообще не читается, он просто открывается для чтения по дескриптору файла 0 (stdin) процесса, в котором он выполняетсяbinary
.В:
В зависимости от оболочки,
binary
stdin будет либо удаленным временным файлом (AT & T ksh, zsh, bash ...), который содержитtest\n
как положено туда оболочкой, так и концом чтения канала (dash
,yash
; и оболочка пишетtest\n
параллельно на другом конце трубы). В вашем случае, если вы используетеbash
, это будет временный файл.В:
В зависимости от оболочки,
binary
stdin будет либо концом чтения канала, либо одним концом пары сокетов, где направление записи отключено (ksh93) иcat
записывает содержимоеfile
другого конца.Когда stdin - обычный файл (временный или нет), он доступен для поиска.
binary
может идти в начало или конец, перематывать и т. д. Он также может отображать его, делать что-тоioctl()s
вроде FIEMAP / FIBMAP (при использовании<>
вместо<
него он может обрезать / пробивать отверстия и т. д.).трубы и пары сокетов, с другой стороны, являются средством межпроцессного взаимодействия, кроме данных мало что
binary
можно сделатьread
(хотя есть также некоторые операции, например, некоторые специфичные для канала,ioctl()
которые он может выполнять с ними, а не с обычными файлами) ,В большинстве случаев, это недостающее способность ,
seek
что приводит к приложениям к сбою / жалуются при работе с трубами, но это может быть любой из других системных вызовов, которые действительны для обычных файлов , но не на различных типах файлов (какmmap()
,ftruncate()
,fallocate()
) , В Linux также есть большая разница в поведении, когда вы открываете,/dev/stdin
когда fd 0 находится в канале или в обычном файле.Существует множество команд, которые могут работать только с доступными для поиска файлами, но в этом случае это обычно не относится к файлам, открытым на их стандартном диске.
unzip
Необходимо прочитать индекс, хранящийся в конце файла, а затем выполнить поиск в файле для чтения членов архива. Но здесь файл (обычный в первом случае, труба во втором) задается в качестве аргумента путиunzip
иunzip
открывает его сам (обычно на fd, отличном от 0), вместо того, чтобы наследовать fd, уже открытый родителем. Он не читает zip-файлы со своего стандартного устройства. stdin в основном используется для взаимодействия с пользователем.Если вы запустите свой
binary
без перенаправления по приглашению интерактивной оболочки, запущенной в эмуляторе терминала, тоbinary
stdin будет унаследован от его родительской оболочки, которая сама унаследует его от своего родительского эмулятора терминала и будет Устройство pty открывается в режиме чтения + записи (что-то вроде/dev/pts/n
).Эти устройства также не доступны для поиска. Таким образом, если
binary
при работе с терминала все работает нормально, возможно, проблема не в поиске.Если это 14 должно быть ошибочным (код ошибки, установленный при сбое системных вызовов), то в большинстве систем это будет
EFAULT
( неверный адрес ).read()
Системный вызов потерпит неудачу с этой ошибкой , если попросил прочитать в адрес памяти , который не доступен для записи. Это будет зависеть от того, будет ли fd считывать данные из точек в канал или обычный файл, и обычно будет указывать на ошибку 1 .binary
возможно определяет тип файла, открытого на его стандартном вводе (сfstat()
), и сталкивается с ошибкой, когда он не является ни обычным файлом, ни устройством tty.Трудно сказать, не зная больше о приложении. Запуск его под
strace
(илиtruss
/ илиtusc
аналогичным образом в вашей системе) может помочь нам увидеть, что такое системный вызов, если он здесь не работает.1 Сценарий, предусмотренный Мэтью Ифе в комментарии к вашему вопросу, звучит здесь очень правдоподобно. Цитирую его:
источник
./binary < file
ищется»!open
и он ведет себя так же, как и любой файл, который былopen
отредактирован. Просто так случилось, что он унаследован от родительского процесса, но это не так уж редко.open("/proc/self/fd/0", O_RDWR)
работает, даже на удаленных файлах. Глупый я: 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
отменяет связьfoo
перед запуском a.out со своим перенаправленным stdinfoo
.Вот простой пример программы, которая иллюстрирует ответ Стефана Шазела, используя
lseek(2)
его входные данные:Тестирование:
Трубы недоступны для поиска, и это единственное место, где программа может жаловаться на трубы.
источник
Труба и перенаправление, так сказать, разные животные. Когда вы используете
here-doc
redirection (<<
) или перенаправление stdin,<
текст не появляется из ничего - он фактически входит в файловый дескриптор (или временный файл, если хотите), и именно на него будет указывать stdin двоичного файла.В частности, вот выдержка из
bash's
исходного кода, файл redir.c (версия 4.3):Таким образом, поскольку перенаправление в основном можно рассматривать как файлы, двоичные файлы могут перемещаться по ним или
seek()
легко перемещаться по файлу, переходя к любому байту файла.Каналы, поскольку они представляют собой буферы объемом 64 КиБ (по крайней мере в Linux) с записью в 4096 байт или менее с гарантией, что они являются атомарными, не доступны для поиска, то есть вы не можете свободно перемещаться по ним - только для последовательного чтения. Однажды я реализовал
tail
команду в Python. 29 миллионов строк текста можно найти в микросекундах, если они перенаправлены, но еслиcat
их отредактировать по конвейеру, то ничего не поделаешь - так что все это нужно читать последовательно.Другая возможность заключается в том, что двоичный файл может специально открыть файл и не хочет получать входные данные из канала. Обычно это делается с помощью
fstat()
системного вызова и проверки, поступает ли ввод изS_ISFIFO
типа файла (который обозначает канал / именованный канал).Ваш конкретный двоичный файл, так как мы не знаем, что это такое, вероятно, пытается искать, но не может искать каналы. Рекомендуется ознакомиться с его документацией, чтобы узнать, что именно означает код ошибки 14.
ПРИМЕЧАНИЕ . Некоторые оболочки, такие как dash (Debian Almquist Shell, по умолчанию
/bin/sh
в Ubuntu), осуществляютhere-doc
перенаправление с внутренними каналами , поэтому могут быть недоступны для поиска. Суть остается той же - каналы являются последовательными и не могут легко перемещаться, и попытки сделать это приведут к ошибкам.источник
dash
этого. Этот ответ объясняет наблюдаемое поведение с bash, но это поведение явно не гарантируется для других оболочек.dash
в моей системе. Я не знал об этом ранее. Спасибо за указаниеfstat()
stdin, чтобы проверить, является ли это каналом.stat
берет путь Но на самом деле, просто попыткаlseek
- это, пожалуй, самый разумный способ определить, можно ли искать fd после того, как он уже открыт.Основное отличие заключается в обработке ошибок.
В следующем случае сообщается об ошибке
В следующем случае ошибка не сообщается.
С bash вы все еще можете использовать PIPESTATUS:
Но это доступно только сразу после выполнения команды:
Есть еще одно отличие, когда мы используем функции оболочки вместо двоичных файлов. В
bash
, функции, которые являются частью конвейера, выполняются в подоболочках (за исключением последнего компонента конвейера, еслиlastpipe
опция включена и неbash
является интерактивной), поэтому изменение переменных не оказывает влияния на родительскую оболочку:источник
>
помощью выполняется оболочкой, а с конвейером - командой, которая создает текст. ХОРОШО. Но в этом конкретном вопросе OP использует существующий файл, так что это не проблема, и ясно, что ошибка вызвана двоичным файлом.