Какое простое объяснение того, как работают трубы в Bash? - PullRequest
52 голосов
/ 23 марта 2012

Я часто использую каналы в Bash, например:

dmesg | less

Хотя я знаю, что это выводит, он берет dmesg и позволяет мне прокручивать его с помощью less, я не понимаю, что| делает.Является ли это просто противоположностью >?

  • Существует ли простое или метафорическое объяснение того, что делает |?
  • Что происходит, когда несколько каналов используются водна строка?
  • Является ли поведение каналов постоянным везде, где оно появляется в скрипте Bash?

Ответы [ 8 ]

66 голосов
/ 23 марта 2012

Канал Unix соединяет файловый дескриптор STDOUT (стандартный вывод) первого процесса со STDIN (стандартный ввод) второго.Затем происходит то, что, когда первый процесс записывает в свой STDOUT, этот вывод может быть немедленно прочитан (из STDIN) вторым процессом.

Использование нескольких каналов ничем не отличается от использования одной трубы.Каждый канал независим и просто связывает STDOUT и STDIN смежных процессов.

Ваш третий вопрос немного неоднозначен.Да, каналы, как таковые, согласованы везде в скрипте bash.Однако символ трубы | может представлять разные вещи.Двойная труба (||), например, представляет оператор "или".

18 голосов
/ 05 октября 2015

В Linux (и Unix в целом) у каждого процесса есть три файловых дескриптора по умолчанию:

  1. fd # 0 Представляет стандартный ввод процесса
  2. fd # 1 Представляет стандартвывод процесса
  3. fd # 2 Представляет стандартную ошибку процесса

Обычно при запуске простой программы эти файловые дескрипторы по умолчанию настраиваются следующим образом:

  1. вход по умолчанию считывается с клавиатуры
  2. Стандартный выход настроен для монитора
  3. Стандартная ошибка настроена также для монитора

Bash предоставляет несколько операторов для изменения этого поведения (посмотрите на операторы>, >> и <, например).Таким образом, вы можете перенаправить вывод на что-то, отличное от стандартного вывода, или прочитать ваш ввод из другого потока, отличного от клавиатуры.Особенно интересен случай, когда две программы <em>взаимодействуют таким образом, что одна использует выходные данные другой в качестве входных данных.Чтобы упростить эту совместную работу, Bash предоставляет трубочному оператору |.Обратите внимание на использование сотрудничества вместо цепочки .Я избегал использования этого термина, так как на самом деле канал не является последовательным .Обычная командная строка с конвейерами имеет следующий аспект:

    > program_1 | program_2 | ... | program_n

Приведенная выше командная строка немного вводит в заблуждение: пользователь может подумать, что program_2 получает свой ввод после завершения выполнения program_1, что не является правильным,Фактически, bash запускает ALL программ параллельно и настраивает входы-выходы соответственно, так что каждая программа получает свой вход от предыдущей и передает свой вывод следующей (в командной строке).установленный порядок).

Ниже приведен простой пример из Создание канала в C создания канала между родительским и дочерним процессами.Важной частью является вызов функции pipe () и то, как родитель закрывает fd 1 (сторона записи), и как дочерний элемент закрывает fd 1 (сторона записи).Обратите внимание, что канал является однонаправленным каналом связи.Таким образом, данные могут передаваться только в одном направлении: fd 1 в направлении fd [0].За дополнительной информацией обращайтесь к странице справочника pipe ().

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(void)
{
    int     fd[2], nbytes;
    pid_t   childpid;
    char    string[] = "Hello, world!\n";
    char    readbuffer[80];

    pipe(fd);

    if((childpid = fork()) == -1)
    {
            perror("fork");
            exit(1);
    }

    if(childpid == 0)
    {
            /* Child process closes up input side of pipe */
            close(fd[0]);

            /* Send "string" through the output side of pipe */
            write(fd[1], string, (strlen(string)+1));
            exit(0);
    }
    else
    {
            /* Parent process closes up output side of pipe */
            close(fd[1]);

            /* Read in a string from the pipe */
            nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
            printf("Received string: %s", readbuffer);
    }

    return(0);
}

Последнее, но не менее важное, если у вас есть командная строка в форме:

> program_1 | program_2 | program_3

ReturnКод всей строки установлен на команду last .В этом случае программа_3.Если вы хотите получить промежуточный код возврата, вы должны установить pipefail или получить его из PIPESTATUS .

15 голосов
/ 23 марта 2012

Каждый стандартный процесс в Unix имеет как минимум три файловых дескриптора , которые похожи на интерфейсы :

  • Стандартный вывод, то есть место, где процесс печатает свои данные (большую часть времени это консоль, то есть ваш экран или терминал).
  • Стандартный ввод, из которого он получает свои данные (в большинстве случаев это может быть что-то похожее на вашу клавиатуру).
  • Стандартная ошибка, это то место, куда отправляются ошибки, а иногда и другие внеполосные данные. Сейчас это не интересно, потому что трубы обычно не справляются с этим.

Канал соединяет стандартный вывод процесса слева со стандартным входом процесса справа. Вы можете думать об этом как о специальной программе, которая заботится о копировании всего, что печатает одна программа, и передаче ее следующей программе (той, которая находится после символа канала). Это не совсем так, но это достаточно адекватная аналогия.

Каждый канал работает ровно на две вещи: стандартный выходной поток идет слева и ожидаемый входной поток справа. Каждый из них может быть присоединен к одному процессу или другому биту конвейера, что имеет место в многопоточной командной строке. Но это не имеет отношения к реальной работе трубы; каждая труба делает свое.

Оператор перенаправления (>) делает что-то связанное, но проще: по умолчанию он отправляет стандартный вывод процесса прямо в файл. Как вы можете видеть, это не противоположность трубы, но фактически дополняет. Противоположностью > является неудивительно <, который берет содержимое файла и отправляет его на стандартный ввод процесса (воспринимает его как программу, которая читает файл побайтно и вводит его в процесс для вы).

6 голосов
/ 23 марта 2012

Канал принимает вывод процесса, под выводом я подразумеваю стандартный вывод (stdout в UNIX) и передает его на стандартный ввод (stdin) другого процесса. Это не противоположность простого правого перенаправления >, целью которого является перенаправление вывода на другой выход.

Например, возьмите команду echo в Linux, которая просто печатает строку, переданную в параметре в стандартный вывод. Если вы используете простой редирект, например:

echo "Hello world" > helloworld.txt

оболочка перенаправит обычный вывод, изначально предназначенный для вывода на стандартный вывод, и напечатает его непосредственно в файл helloworld.txt.

Теперь возьмем этот пример, который включает трубу:

ls -l | grep helloworld.txt

Стандартный вывод команды ls будет выводиться при вводе grep, как это работает?

Такие программы, как grep, когда они используются без каких-либо аргументов, просто читают и ожидают передачи чего-либо на их стандартный ввод (stdin). Когда они что-то ловят, например, вывод команды ls, grep работает нормально, находя совпадения с тем, что вы ищете.

2 голосов
/ 23 марта 2012

Трубы очень просты, как это.

У вас есть выход одной команды.Вы можете предоставить этот вывод как ввод в другую команду, используя pipe.Вы можете передать столько команд, сколько хотите.

ex: ls |grep мой |grep files

Сначала перечисляются файлы в рабочем каталоге.Этот вывод проверяется командой grep на наличие слова «my».Вывод этого теперь во вторую команду grep, которая наконец ищет слово «файлы».Это оно.

2 голосов
/ 23 марта 2012
  • | помещает STDOUT команды с левой стороны в STDIN команды правой стороны.

  • Если вы используете несколько каналов,просто цепь труб.Выход первой команды настроен на ввод второй команды.Выход второй команды настроен на ввод следующей команды.И так далее.

  • Он доступен во всех интерпретаторах команд на основе Linux / вдов.

2 голосов
/ 23 марта 2012

Трубный оператор берет вывод первой команды и «передает» ее второй, соединяя stdin и stdout. В вашем примере вместо вывода команды dmesg, идущей в stdout (и выбрасывающей ее на консоль), она переходит прямо к вашей следующей команде.

1 голос
/ 23 марта 2012

Если вы рассматриваете каждую команду unix как отдельный модуль,
но вам нужно, чтобы они общались друг с другом, используя текст как последовательный интерфейс,
как это можно сделать?

cmd                       input                    output

echo "foobar"             string                   "foobar" 
cat "somefile.txt"        file                     *string inside the file*
grep "pattern" "a.txt"    pattern, input file      *matched string*

Можно сказать, | - это метафора для передачи эстафеты в эстафете.
Его даже в форме, как один!
cat -> echo -> less -> awk -> perl аналогично cat | echo | less | awk | perl.

cat "somefile.txt" | echo
cat передать свой вывод для использования echo.

Что происходит, когда имеется более одного входа?
cat "somefile.txt" | grep "pattern"
Существует неявное правило, которое гласит: «передайте его как входной файл вместо pattern » для grep.
Вы будете постепенно развивать зрение, чтобы знать, какой параметр является каким из опыта.

...