Труба, стандартный ввод и аргументы командной строки в Bash

считаем:

command1 | command2

используется ли вывод command1 в качестве стандартного ввода command2 или в качестве аргументов командной строки для command2?

например,

cat test.sh | grep "hehe"

какова его эквивалентная форма без использования трубы?

пробовал

grep "hehe" $(cat test.sh)

и это кажется неправильным.

4 ответов


grep "hehe" < test.sh

перенаправление ввода-работает только для одного файла, конечно, тогда как cat работает для любого количества входных файлов.


учитывать указания:

grep "hehe" $(cat test.sh)
grep "hehe" `cat test.sh`

они эквивалентны в этом контексте; гораздо проще использовать '$(cmd)' нотация во вложенных видах использования, таких как:

x=$(dirname $(dirname $(which gcc)))
x=`dirname \`dirname \\`which gcc\\`\``

(это дает вам базовый каталог, в котором установлен GCC, в случае, если вам интересно.)

на grep пример, что бывает так, что содержимое test.sh читается и разбивается на слова, разделенные пробелом, и каждое такое слово предоставляется в качестве аргумента grep. С grep обрабатывает слова после "hehe" (где grep, конечно, не не см. двойные кавычки - и в этом случае они не нужны; как правило, используйте одинарные кавычки, а не двойные кавычки, особенно вокруг сложных строк, таких как регулярные выражения, которые часто используют метасимволы оболочки)... Как и я. говоря:grep обрабатывает слова после "hehe" как имена файлов и пытается открыть каждый файл, как правило, неудачно, потому что файлы не существуют. Вот почему эти обозначения неуместны в данном контексте.


после повторного рассмотрения вопроса, есть еще что можно сказать - это еще не было сказано.

во-первых, многие команды Unix предназначены для работы в качестве "фильтров"; они читают ввод из некоторых файлов, преобразуют его каким-то образом и пишут результат на стандартный выход. Такие команды предназначены для использования в командных конвейерах. Примеры включают:

  • кошки
  • grep
  • трофф и родственники
  • на awk (с оговорками)
  • sed
  • вроде

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

есть несколько чистых фильтров - tr одно такое-что строго прочитайте стандартный входной сигнал и напишите к стандартному выходу.

другие команды имеют различное поведение. Eric Raymond предоставляет таксономию для типов команд в "искусство UNIX Программирование".

некоторые команды генерируют списки имен файлов на стандартном выходе-две классики ls и find.

иногда требуется применить выходные данные генератора имен файлов в качестве аргументов командной строки для фильтра. Есть программа, которая делает это автоматически - это xargs.

классически, можно использовать:

find . -name '*.[chyl]' | xargs grep -n magic_name /dev/null

это создаст полный список файлов с расширениями'.c', '.h','.y' и '.l' (источник C, заголовки, файлы Yacc и Lex). Как список читается xargs, он будет создавать командные строки с grep -n magic_name /dev/null в начале и каждое слово (разделенных пробелом) в качестве аргумента.

в старые времена имена файлов Unix не включали пробелы. Под влиянием Mac и Windows, такие пространства теперь являются общим местом. Версии GNU find и xargs дополнительные параметры, чтобы справиться с этим проблема:

find . -name '*.[chyl]' -print0 | xargs -0 grep -n magic_name /dev/null

в '-print0' опция означает "печать имен файлов, завершенных нулем' \0 '"(потому что единственными символами, которые не могут отображаться в (простом) имени файла, являются " / "и NUL, и, очевидно," / " может отображаться в именах путей). Соответствующее "-0 говорит xargs искать имена, завершенные нулем, вместо имен, разделенных пробелом.


другой формой перенаправления является подстановка процессов.

grep "hehe" <(cat test.sh)

эквивалентно:

grep "hehe" test.sh

которые оба смотрят на содержимое .

в то время как, как было отмечено, эта команда:

grep "hehe" $(cat test.sh)

ищет файлы в test.sh и использует их в качестве аргументов grep. Так что если test.sh состоит из:

scriptone
scripttwo

затем grep собирается искать "хе-хе" в содержание каждого из этих файлов.


Он используется как stdin.

попробуй:

grep "hehe" - $(cat test.sh)

это может быть неправильно; я не могу проверить это на этом компьютере. Если вы делаете это без канала, как вы пытались, grep обрабатывает последний аргумент как имя файла, т. е. ищет файл с именем [contents of test.sh]. Если вы передадите его A - (или не поставите последний аргумент), вы скажете ему использовать stdin в качестве файла.

вы также можете просто передать grep файл для сканирования:

grep "hehe" test.sh

...но ты, кажется, спрашиваешь. более обобщенный вопрос bash, а не вопрос использования grep, так что это, вероятно, не слишком полезно.


что такое эквивалент канала bash с использованием аргументов командной строки?

каналы и аргументы командной строки-это разные формы ввода, которые не являются взаимозаменяемыми. Если программа позволяет вам иметь эквивалентные формы обоих, это выбор только этой программы. (В исходном коде аргументы командной строки отображаются в виде текста переменной, а каналы - в виде открытых файлов, включая stdin и stdout. Синтаксис перенаправления ввода-вывода Bash, как используется здесь lateron, технически делает не принадлежат аргументам командной строки, даже если они написаны рядом с ними в командной строке ...)

но давайте будем педантичными, а также ответим на это:

что такое эквивалент трубы bash без использования символа трубы bash?

ответ: cat test.sh | grep "hehe" эквивалентно

grep "hehe" < <(cat test.sh)

объяснение:

  • каналы перенаправляют stdout одной команды на stdin другой. Установить источник stdin, мы можем использовать перенаправление ввода (< …) вместо использования этого символа.
  • однако, просто используя перенаправление ввода (grep "hehe" < test.sh) не эквивалентен трубам, потому что он использует в качестве источника для stdin, в то время как трубы используют вывод команды (cat test.sh). Таким образом, кроме того, мы добавляем процесс substitution <(…) заменить ввод из файла на ввод из команды.
  • конечно, пример сбивает с толку, потому что два варианта имеют одинаковые эффекты:

    grep "hehe" < test.sh
    grep "hehe" < <(cat test.sh)
    

    но технически ввод из файла по-прежнему отличается от ввода из вывода команды, которая получает свой ввод из файла.

источник: Advanced Bash Scripting Manual, раздел о подстановке процессов (начните читать "некоторые другие обычаи").