Pipe, стандартный ввод и аргументы командной строки в Bash - PullRequest
7 голосов
/ 01 октября 2009

Рассмотрим:

command1 | command2

Используется ли вывод команды 1 как стандартный ввод команды 2 или как аргументы командной строки для команды 2?

Например,

cat test.sh | grep "hehe"

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

Я пытался

grep "hehe" $(cat test.sh)

и, похоже, это не правильно.

Ответы [ 4 ]

14 голосов
/ 01 октября 2009
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 (с оговорками)
  • 1041 * СЭД *
  • 1043 * рода *

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

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

Другие команды имеют другое поведение. Эрик Рэймонд предоставляет таксономию для типов команд в " Искусство программирования UNIX ".

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

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

Классически вы бы использовали:

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

Это приведет к созданию полного списка файлов с расширениями '.c', '.h', '.y' и '.l' (C source, заголовки, файлы 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' означает «печатать имена файлов, оканчивающиеся NUL '\ 0'" (потому что единственными символами, которые не могут появиться в (простом) имени файла, являются '/' и NUL, и, очевидно, ' / 'может появляться в именах путей). Соответствующий '-0' говорит xargs искать имена, оканчивающиеся NUL, вместо имен, разделенных пробелом.

6 голосов
/ 01 октября 2009

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

grep "hehe" <(cat test.sh)

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

grep "hehe" test.sh

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

Пока, как уже было отмечено, эта команда:

grep "hehe" $(cat test.sh)

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

scriptone
scripttwo

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

1 голос
/ 01 октября 2009

Используется как стандартный.

Попытка:

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

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

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

grep "hehe" test.sh

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

0 голосов
/ 25 февраля 2014

Что эквивалентно каналу bash, использующему аргументы командной строки?

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

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

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

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

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

Пояснение:

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

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

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

Источник: Руководство по расширенному написанию сценариев Bash, раздел о замене процесса (начните чтение с "Некоторые другие применения").

...