Linux read from pipe

Linux pipes tips & tricks

Pipe — что это?

Pipe (конвеер) – это однонаправленный канал межпроцессного взаимодействия. Термин был придуман Дугласом Макилроем для командной оболочки Unix и назван по аналогии с трубопроводом. Конвейеры чаще всего используются в shell-скриптах для связи нескольких команд путем перенаправления вывода одной команды (stdout) на вход (stdin) последующей, используя символ конвеера ‘|’:

grep выполняет регистронезависимый поиск строки “error” в файле log, но результат поиска не выводится на экран, а перенаправляется на вход (stdin) команды wc, которая в свою очередь выполняет подсчет количества строк.

Логика

Конвеер обеспечивает асинхронное выполнение команд с использованием буферизации ввода/вывода. Таким образом все команды в конвейере работают параллельно, каждая в своем процессе.

Размер буфера начиная с ядра версии 2.6.11 составляет 65536 байт (64Кб) и равен странице памяти в более старых ядрах. При попытке чтения из пустого буфера процесс чтения блокируется до появления данных. Аналогично при попытке записи в заполненный буфер процесс записи будет заблокирован до освобождения необходимого места.
Важно, что несмотря на то, что конвейер оперирует файловыми дескрипторами потоков ввода/вывода, все операции выполняются в памяти, без нагрузки на диск.
Вся информация, приведенная ниже, касается оболочки bash-4.2 и ядра 3.10.10.

Простой дебаг

Исходный код, уровень 1, shell

Т. к. лучшая документация — исходный код, обратимся к нему. Bash использует Yacc для парсинга входных команд и возвращает ‘command_connect()’, когда встречает символ ‘|’.
parse.y:
Также здесь мы видим обработку пары символов ‘|&’, что эквивалентно перенаправлению как stdout, так и stderr в конвеер. Далее обратимся к command_connect():make_cmd.c:
где connector это символ ‘|’ как int. При выполнении последовательности команд (связанных через ‘&’, ‘|’, ‘;’, и т. д.) вызывается execute_connection():execute_cmd.c:

PIPE_IN и PIPE_OUT — файловые дескрипторы, содержащие информацию о входном и выходном потоках. Они могут принимать значение NO_PIPE, которое означает, что I/O является stdin/stdout.
execute_pipeline() довольно объемная функция, имплементация которой содержится в execute_cmd.c. Мы рассмотрим наиболее интересные для нас части.
execute_cmd.c:
Таким образом, bash обрабатывает символ конвейера путем системного вызова pipe() для каждого встретившегося символа ‘|’ и выполняет каждую команду в отдельном процессе с использованием соответствующих файловых дескрипторов в качестве входного и выходного потоков.

Исходный код, уровень 2, ядро

Обратимся к коду ядра и посмотрим на имплементацию функции pipe(). В статье рассматривается ядро версии 3.10.10 stable.
fs/pipe.c (пропущены незначительные для данной статьи участки кода):
Если вы обратили внимание, в коде идет проверка на флаг O_NONBLOCK. Его можно выставить используя операцию F_SETFL в fcntl. Он отвечает за переход в режим без блокировки I/O потоков в конвеере. В этом режиме вместо блокировки процесс чтения/записи в поток будет завершаться с errno кодом EAGAIN.

Максимальный размер блока данных, который будет записан в конвейер, равен одной странице памяти (4Кб) для архитектуры arm:
arch/arm/include/asm/limits.h:
Для ядер >= 2.6.35 можно изменить размер буфера конвейера:
Максимально допустимый размер буфера, как мы видели выше, указан в файле /proc/sys/fs/pipe-max-size.

Tips & trics

В примерах ниже будем выполнять ls на существующую директорию Documents и два несуществующих файла: ./non-existent_file и. /other_non-existent_file.

Перенаправление и stdout, и stderr в pipe

или же можно использовать комбинацию символов ‘|&’ (о ней можно узнать как из документации к оболочке (man bash), так и из исходников выше, где мы разбирали Yacc парсер bash):

Перенаправление _только_ stderr в pipe

Shoot yourself in the foot
Важно соблюдать порядок перенаправления stdout и stderr. Например, комбинация ‘>/dev/null 2>&1′ перенаправит и stdout, и stderr в /dev/null.

Получение корректного кода завершения конвейра

По умолчанию, код завершения конвейера — код завершения последней команды в конвеере. Например, возьмем исходную команду, которая завершается с ненулевым кодом:
И поместим ее в pipe:
Теперь код завершения конвейера — это код завершения команды wc, т.е. 0.

Читайте также:  Linux shell script if else

Обычно же нам нужно знать, если в процессе выполнения конвейера произошла ошибка. Для этого следует выставить опцию pipefail, которая указывает оболочке, что код завершения конвейера будет совпадать с первым ненулевым кодом завершения одной из команд конвейера или же нулю в случае, если все команды завершились корректно:
Shoot yourself in the foot
Следует иметь в виду “безобидные” команды, которые могут вернуть не ноль. Это касается не только работы с конвейерами. Например, рассмотрим пример с grep:
Здесь мы печатаем все найденные строки, приписав ‘new_’ в начале каждой строки, либо не печатаем ничего, если ни одной строки нужного формата не нашлось. Проблема в том, что grep завершается с кодом 1, если не было найдено ни одного совпадения, поэтому если в нашем скрипте выставлена опция pipefail, этот пример завершится с кодом 1:
В больших скриптах со сложными конструкциями и длинными конвеерами можно упустить этот момент из виду, что может привести к некорректным результатам.

Источник

Example of using named pipes in Linux shell (Bash)

Can someone post a simple example of using named pipes in Bash on Linux?

5 Answers 5

One of the best examples of a practical use of a named pipe.

Another useful behavior is using netcat as a proxy. Both ports and hosts can be redirected. Look at this example:

Port 12345 represents the request.

This starts a nc server on port 12345 and all the connections get redirected to google.com:80 . If a web browser makes a request to nc , the request will be sent to google but the response will not be sent to the web browser. That is because pipes are unidirectional. This can be worked around with a named pipe to redirect the input and output.

Here are the commands:

The first command creates the pipe.

The second command writes to the pipe (blocking). The & puts this into the background so you can continue to type commands in the same shell. It will exit when the FIFO is emptied by the next command.

The last command reads from the pipe.

Open two different shells, and leave them side by side. In both, go to the /tmp/ directory:

In the first one type:

In the second one, type:

First shell won’t give you any prompt back until you execute the second part of the code in the second shell. It’s because the fifo read and write is blocking.

You can also have a look at the FIFO type by doing a ls -al myPipe and see the details of this specific type of file.

Next step would be to embark the code in a script!

Creating a named pipe

On Unix-likes named pipe (FIFO) is a special type of file with no content. The mkfifo command creates the pipe on a file system (assigns a name to it), but doesn’t open it. You need to open and close it separately like any other file.

Using a named pipe

Named pipes are useful when you need to pipe from/to multiple processes or if you can’t connect two processes with an anonymous pipe. They can be used in multiple ways:

In parallel with another process:

Here writer runs along the reader allowing real-time communication between processes.

In fact, communication through a pipe can be sequential, but it’s limited to a buffer size of 64 KB.
It’s preferable to use descriptors to transfer multiple pieces of data in order to reduce overhead.

FD allows data transfer to start before the shell is ready to receive it. Required when used sequentially.
Signal should be sent before the data to prevent a deadlock if pipe buffer fills up.

Destroying a named pipe

The pipe itself (and its content) gets destroyed when all descriptors to it are closed. What’s left is just a name.
To make the pipe anonymous and unavailable under the given name (can be done when the pipe is still open) you could use the rm console command (this is the opposite of mkfifo command):

Читайте также:  Как установить касперского после переустановки windows

Источник

Linux. Python. Read from named pipe

I am having issues replicating some functionality in python.
Working under ubuntu.
I have a program in c++ that does stuff, and every once in a while it writes formatted data to a named pipe. I also have a c++ program that reads from that named pipe. No issues there. However I have almost no clue as to how to replicate the same thing (only the reading part) in python.

Here’s the c++ code I use to read:

This c++ code works exactly as I want it to: it reads the bytes from the named pipe, converts them to the corresponding numbers, makes them available for me to play with.
I tried to do the same in python, but failing.
First attempt at python code:

Second attempt at python code (gives the same wrong results):

In the second attempt I was expecting to read up to the first linebreak (i can easily have the c++ producer put a linebreak at the end of each «packet»). However this always reads empty lines.

My question is about getting in python the functionalities I have in c++. I can see this happening in two ways:

  • Fix the way I use the pipe on the python side, by actually reading the bytes as I do in c++ and marshaling those to the correct data types and values.
  • Have the python «load» a buffer of bytes until it receives the linebreak, then extract my numbers from there.

Either way, I don’t understand why the read and the readline are returning those blank strings, nor I know how to handle them.

I hope some of you guys can help me. Feel free to ask for clarifications and more information.

Источник

Using data read from a pipe instead than from a file in command options

Per man definition, this command gets the input from a file.

Suppose that FILENAME is a file containing a list of filenames, as it was generated using ls > FILENAME .

How can I, instead, feed the command with the result of ls directly? In my head something like this should be possible:

But it doesn’t, the output of ls doesn’t get hooked as an argument. Output:

How could I obtain the desired effect?

5 Answers 5

This is dependent on the command. Some commands that read from a file expect that the file be a regular file, whose size is known in advance, and which can be read from any position and rewinded. This is unlikely if the contents of the file is a list of file names: then the command will probably be content with a pipe which it will just read sequentially from start to finish. There are several ways to feed data via a pipe to a command that expects a file name.

Many commands treat — as a special name, meaning to read from standard input rather than opening a file. This is a convention, not an obligation.

Many unix variants provide special files in /dev that designate the standard descriptors. If /dev/stdin exists, opening it and reading from it is equivalent to reading from standard input; likewise /dev/fd/0 if it exists.

If your shell is ksh, bash or zsh, you can make the shell deal with the business of allocating some file descriptor. The main advantage of this method is that it’s not tied to standard input, so you can use standard input for something else, and you can use it more than once.

If the command expects the name to have a particular form (typically a particular extension), you can try to fool it with a symbolic link.

Or you can use a named pipe.

Note that generating a list of files with ls is problematic because ls tends to mangle file names when they contain unprintable characters. printf ‘%s\n’ * is more reliable — it’ll print every byte literally in file names. File names containing newlines will still cause trouble, but that’s unavoidable if the command expects a list of file names separated by newlines.

Читайте также:  Сами открываются вкладки windows 10

Источник

dash: read variable from pipe

In bash or zsh , I can use following syntax to read from pipe into variables:

which will print AAA

Why does the same not work in /bin/sh ?

I am using /bin/sh -> /bin/dash dash in Debian

4 Answers 4

Why does the same not work in ‘/bin/sh’ ?

Assigning variables in a pipe does not work as expected in sh and bash because each command of a pipe runs in a subshell. Actually, the command does work, X and Y get declared, but they are not available outside the pipe.

The following will work:

But in your case:

Some useful links:

in bash or zsh, I can use following syntax to read from pipe into variables.

No, you can’t. Not in Bash with the default settings.

Bash runs all the commands in a pipeline in separate subshell environments, so the changes to shell variables aren’t visible outside the pipeline. Dash is similar here.

Zsh and ksh (AT&T implementations, not pdksh or derivatives) run the last command of the pipeline in the main shell environment, so there that works:

In Bash, you can shopt -s lastpipe to have it do what ksh and zsh do (only works in non-interactive shells though):

But I don’t think there’s such an option for Dash.

In Bash you could also use process substitution instead of the pipe, but that’s not an option in Dash either.

The workarounds would revolve around making the right-hand side of the loop a compound statement or a function, and so using the value read from the pipe in the same environment it was read in.

Or use a here document:

No, this does not set X and Y (after the semicolon).

The same happens in dash.

To get it to work in dash you need to resort to older solutions (here-doc):

As a command to try shells (sadly the newlines can not be removed (no one-liner)):

That works exactly the same in dash, zsh, ksh, and many others:

Newer alternatives (that do not work in dash) to the here-doc may be:

here-string (works in bash, ksh, zsh):

Process substitution (works in bash, ksh, zsh):

An alternative that prints the value and works in dash, but does not keep the variable set in dash or bash (but does in ksh and zsh), is:

Note that this last solution may be set to keep the variables set in bash with the lastpipe option (not for interactive use) or in ksh/zsh by default:

Be careful with variable assignments from a process in a pipeline. The POSIX standard does not require a specific behavior.

Modern shells like ksh93 and recent versions of the Bourne Shell let the main shell be the parent of both processes in your pipeline and in case the rightmost process is a builtin command, this command is even run in the main shell.

Another variant is to use the above method but to always run the rightmost command in another process.

The old version is how the original Bourne Shell worked: The shell forks and the forked process creates all other proceses from the pipe and finally converts into the rightmost process.

The last version needs a lot less code than the others but is slower. Because of the code size, this was used in 1976.

The first variant is the fastest variant but needs more code than the others, but it it the only variant that runs the variable assignment in the orginal shell process, which is required to have the modified variable value in the main shell.

Источник

Оцените статью