Вопрос или проблема
Как перенаправить вывод команды в другие команды?
Пример:
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’.
Как обойти ограничения
Чтобы перенаправить вывод одной команды как аргументы другой команды, вы можете использовать несколько подходов:
-
Использование
xargs
:Команда
xargs
позволяет вам прочитать аргументы из стандартного ввода и сформировать команды, которые вы хотите выполнить. Например:ls | xargs echo
Это вызовет
echo
с теми элементами, которые выводитls
. -
Командная подстановка (
$()
или обратные кавычки):Вы можете использовать командную подстановку, которая выполняет команду и подставляет её вывод в другую команду:
echo $(ls)
Либо более безопасный вариант:
echo "$(ls -lrt)"
-
Циклы с использованием команды
read
:Если вам необходимо поочередно обработать вывод одной команды, можно использовать цикл
while
с командойread
:ls | while read OUT; do echo $OUT; done
-
Циклы
for
:Также вы можете использовать конструкцию
for
:for i in $(ls); do echo $i; done
Обратите внимание, что данный подход может быть менее производительным, чем использование
xargs
.
Заключение
Разница в том, как команды обрабатывают стандартный ввод, объясняет, почему некоторые команды работают с конвейерами, а другие нет. Команды, которые не читают стандартный ввод, такие как echo
, просто игнорируют его. Чтобы перенаправить вывод одной команды как аргументы другой, изучите упомянутые подходы: xargs
, командную подстановку и конструкции циклов.