3

Как многие из нас наверняка знают, это хорошая идея, чтобы ваша программа принимала ввод stdin. Очень многие программы допускают такую среду * nix. Это позволяет нам делать классные вещи, такие как echo "foo" | less Довольно часто можно найти этот cat barfile | baz логически эквивалентен baz barfile как за кулисами он все равно просто читает строки.

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

Теперь мой вопрос: существует ли временный файловый канал?

Теперь, с моей буквально несуществующей способностью писать Bash и около 5 минут в Google, я придумал это

#!/bin/bash
if [ $# -ne 2 ]; then exit 1; fi
f=$(mktemp)
($1) > $f
if [ $? -eq 0 ]; then ($2 $f); else exit $?; fi
rm $f

Вызывая этот fpipe, мы можем сделать что-то вроде fpipe 'wget -O- www.example.com' baz где baz - это программа, в которую мы не можем передать, но можем сделать baz file .

Мой вопрос: как мы можем добиться большего успеха? Я подозреваю, что, обладая большим знанием Bash, переписать приведенный выше скрипт для получения любого количества аргументов будет довольно тривиально (так что мы можем делать такие вещи, как fpipe 'foo x' bar baz где с помощью конвейера мы можем сделать что-то вроде foo x | bar | baz . Бах, мы могли бы, вероятно, смешать их и получить такие вещи, как fpipe 'wget -O- www.example.org | rev' baz .

Существует ли существующая конструкция, которая достигает этого? Мне кажется, я видел конструкцию вида foo x > bar < baz или что-то в этом роде. Я бы подумал, что это довольно распространенная проблема, но мои поиски ничего не поднимают. Это означает, что я либо недостаточно усердно занимаюсь поиском, либо упускаю что-то довольно очевидное.

Если для этого не существует «Правильного пути», можно ли определить удобный синтаксис? Скажем, foo x |> baz | rev где |> значительной степени напрямую переводится в мой скрипт.

PS: я знаю, что мой /script / очень хрупкий и наивный (например, выход из не 0); не стесняйтесь размещать лучшее.

3 ответа3

5

С достаточно недавней версией Bash:

$ baz <(wget -O- www.example.com)

Это называется процессом замены.

4

Это будет работать (в принципе) в любой оболочке, но для этого требуется «достаточно свежая версия» операционной системы:

wget -O- www.example.com | baz /dev/stdin

Когда существует /dev/stdin , это, как правило, символическая ссылка на /proc/self/fd/0 , поэтому, если у вашего ящика нет /dev/stdin , проверьте наличие /proc/self/fd/0 .


Конечно, изобретение труб было одним из великих нововведений Unix. 1 Временные промежуточные файлы в принципе хороши, но могут иметь проблемы с производительностью, и в случае

program_that_produces_gobs_of_output | grep regular_expression_that_matches_very_few_lines

у вас может возникнуть проблема с невозможностью разместить файл (весь вывод первой команды) на диске.  Итак, лучшее из обоих миров - использовать именованную трубу.  Это позволяет вам захватить стандартный вывод из какой-либо программы 2 и предоставить его в качестве входных данных для другой программы в качестве параметра пути:

f=$(mktemp)  &&  rm -f "$f"  &&  mkfifo "$f"
$1 >> "$f" &
$2 "$f"
rm -f "$f"

Первый rm -f "$f" необходим, потому что mktemp не только генерирует имя файла; он создает файл.  Если мы не удалим его, mkfifo потерпит неудачу.  (В качестве альтернативы, мы могли бы использовать mktemp -u , который генерирует имя файла без создания файла.)
___________
1 Являются ли «изобретенные» каналы Unix несколько спорными.  Согласно «Эволюции системы разделения времени Unix» (Деннис М. Ричи) (PDF), эта концепция существовала в других формах в других местах до появления труб в Unix в 1972 году.

2 или вообще любой выходной поток; например, из cp somefile "$f" или dd … of="$f"

-1

Вот два метода, чтобы ваш скрипт работал одинаково, если заданы аргументы файла или если он должен читать строки из stdin:

1) cat файлы, используйте специальное имя файла "-", если в командной строке не указаны файлы

#!/bin/bash
[[ $# -eq 0 ]] && set -- "-"
n=0
while read line; do
    echo "$((++n)) $line"
done < <(cat "$@")

2) если файлы указаны, поместите их содержимое в поток stdin скрипта

#!/bin/bash
[[ $# -gt 0 ]] && exec 0< <(cat "$@")
n=0
while read line; do
    echo "$((++n)) $line"
done

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .