Как перенаправить вывод команды в другие команды?

Вопрос или проблема

Как перенаправить вывод команды в другие команды?

Пример:

ls | echo ничего не выводит (на самом деле пустую строку). Я ожидал, что он выведет список файлов.

ls | grep 'foo', с другой стороны, работает как ожидалось (выводит файлы с ‘foo’ в имени).

В таких ситуациях я делаю что-то вроде:
ls | while read OUT; do echo $OUT; done, но это довольно громоздко.

Почему конвейеры работают с одними командами, но не работают с другими? Как я могу обойти эту проблему?

Существует различие между аргументами командной строки и стандартным вводом. Конвейер соединяет стандартный вывод одного процесса со стандартным вводом другого. Так что

ls | echo

Соединяет стандартный вывод ls со стандартным вводом echo. Всё нормально, верно? Ну, echo игнорирует стандартный ввод и просто выводит свои аргументы командной строки, которых в данном случае нет, на свой стандартный вывод. Результат: ничего.

В этом случае есть несколько решений. Одно из них — использовать команду, которая читает stdin и выводит в stdout, например cat.

ls | cat

Это будет “работать”, в зависимости от того, что вы подразумеваете под “работать”.

Но что насчет общего случая? Что вы действительно хотите, так это преобразовать stdout одной команды в аргументы командной строки другой. Как другие уже упомянули, xargs является каноническим вспомогательным инструментом в этом случае, который читает аргументы командной строки для команды из stdin и формирует команды для выполнения.

ls | xargs echo

Вы также можете использовать команду подстановки $(), чтобы сделать то, что вы хотите.

echo $(ls)

Оба этих инструмента довольно основополагающие для скриптов на оболочке, вам следует изучить оба.

Для полноты картины, как вы указали в вопросе, другой базовый способ преобразования stdin в аргументы командной строки — это встроенная команда оболочки read. Она преобразует “слова” (слова, определяемые переменной IFS) во временную переменную, которую вы можете использовать в любом выполняемом командном запросе.

ls | echo выводит только пустую строку, потому что echo не читает никакой ввод; последней командой в конвейере на самом деле является echo, которая ничего не выводит, кроме пустой строки.

В общем:

a | b

гарантирует, что вывод a становится вводом b. Я рекомендую вам прочитать раздел Pipelines в man bash.


Если вы действительно хотите использовать ls и echo вместе, вот некоторые (достаточно бесполезные) примеры:

ls | xargs -L 1 echo
echo `ls`
for i in `ls` ; do echo $i ; done

Если вы хотите echo результат команды ls, попробуйте:

ls | xargs echo

Это вызовет echo с выводом ls.

Почему бы просто не использовать:

echo `ls`

Или гораздо безопаснее:

echo "`ls -lrt`"

Это можно сделать несколькими способами, такими как конвейер |, STDERR 2>, xargs или с помощью подстановки команд $().

Пример использования xargs:

ls | xargs echo

Просто добавлю еще одну опцию, которую я не видел выше.

Иногда xargs не работает для конкретной команды, которую я хочу, поэтому другой вариант — использовать цикл:

for i in $(cmd_that_produces_args_as_list); do cmd_i_want_to_run_on_list ${i} ; done

Я не думаю, что это так быстро, как xargs, но это хорошо работает, когда это необходимо.

Ответ или решение

Чтобы перенаправить вывод одной команды в качестве ввода для другой команды в Unix-подобных операционных системах, используется оператор конвейера (pipe) — символ "|". Тем не менее, не все команды могут обрабатывать ввод через стандартный поток (stdin).

Пример и объяснение

Рассмотрим ваш пример:

ls | echo

Этот конвейер не выведет список файлов, поскольку команда echo не читает стандартный ввод. Вместо этого она просто выводит свои аргументы на стандартный выход (stdout), которые в данном случае отсутствуют. Поэтому вы получаете пустую строку.

В отличие от этого, такая команда:

ls | grep 'foo'

работает корректно, поскольку grep настроен на чтение из стандартного ввода и фильтрацию строк, содержащих ‘foo’.

Как обойти ограничения

Чтобы перенаправить вывод одной команды как аргументы другой команды, вы можете использовать несколько подходов:

  1. Использование xargs:

    Команда xargs позволяет вам прочитать аргументы из стандартного ввода и сформировать команды, которые вы хотите выполнить. Например:

    ls | xargs echo

    Это вызовет echo с теми элементами, которые выводит ls.

  2. Командная подстановка ($() или обратные кавычки):

    Вы можете использовать командную подстановку, которая выполняет команду и подставляет её вывод в другую команду:

    echo $(ls)

    Либо более безопасный вариант:

    echo "$(ls -lrt)"
  3. Циклы с использованием команды read:

    Если вам необходимо поочередно обработать вывод одной команды, можно использовать цикл while с командой read:

    ls | while read OUT; do echo $OUT; done
  4. Циклы for:

    Также вы можете использовать конструкцию for:

    for i in $(ls); do echo $i; done

    Обратите внимание, что данный подход может быть менее производительным, чем использование xargs.

Заключение

Разница в том, как команды обрабатывают стандартный ввод, объясняет, почему некоторые команды работают с конвейерами, а другие нет. Команды, которые не читают стандартный ввод, такие как echo, просто игнорируют его. Чтобы перенаправить вывод одной команды как аргументы другой, изучите упомянутые подходы: xargs, командную подстановку и конструкции циклов.

Оцените материал
Добавить комментарий

Капча загружается...