15

Есть ли способ трубы на выходе из одной функции в другую, но только если есть выход?

6 ответов6

14

Это должно работать для вас

$ --a function-- | [ xargs -r ] --another function--

Пример

$ echo -e "\n\n" | xargs -r ls
$ # No output. ls did not run.
$ echo -e "\n\n1" | xargs -r ls
ls: cannot access 1: No such file or directory

Это просто, но это должно работать для вас. Если ваша "функция" отправит пустую строку или даже новую строку по конвейеру, xargs -r предотвратит переход к "другой функции".

Ссылка на xargs: http://www.oreillynet.com/linux/cmd/cmd.csp?path=x/xargs

-r, --no-run-if-empty
Do not run command if standard input contains only blanks.
9

ifne(1) из moreutils делает именно это. Moreutils доступен как пакет, по крайней мере, в Debian и Ubuntu, возможно, и в других дистрибутивах.

9

Следующая функция ifnotempty свой ввод команде, переданной в качестве аргумента, за исключением того, что она ничего не делает, если ввод пуст. Используйте его, чтобы source --foo в sink --bar , написав source --foo | pipe_if_not_empty sink --bar .

pipe_if_not_empty () {
  head=$(dd bs=1 count=1 2>/dev/null; echo a)
  head=${head%a}
  if [ "x$head" != x"" ]; then
    { printf %s "$head"; cat; } | "$@"
  fi
}

Дизайн заметки:

  • Я ожидаю, что эта реализация будет работать на всех платформах POSIX/Unix, хотя, строго говоря, она не совместима со стандартами: она полагается на то, что dd не читает больше, чем один байт, который, как ей говорят, читает на своем стандартном вводе.
  • Я думаю, что head -c 1 будет подходящей заменой для dd bs=1 count=1 2>/dev/null в Linux.
  • С другой стороны, head -n 1 не подходит, потому что head обычно буферизует свой ввод и может читать больше, чем одну строку, которую он выводит - и, поскольку он читает из канала, лишние байты просто теряются.
  • read -r head и даже read -r -n 1 head не подходит здесь , потому что , если первый символ новой строка, head будет установлена в пустую строку, что делает его невозможно отличить пустой вход и вход , начиная с пустой строкой ,
  • Мы не можем просто написать head=$(head -c 1) потому что, если первый символ является новой строкой, подстановка команды лишит последней новой строки, делая невозможным различение пустого ввода и ввода, начинающегося с пустой строки.
  • В bash, ksh или zsh вы можете заменить cat на </dev/stdin для увеличения производительности микроскопа.

Если вы не возражаете против сохранения всех промежуточных данных в памяти, то здесь приведена несколько более простая реализация pipe_if_not_empty .

pipe_if_not_empty () {
  input=$(cat; echo a);
  if [ "x$input" != x"a" ]; then
    { printf %s "${input%a}"; } | "$@"
  fi
}

Вот немного более простая реализация со следующими оговорками:

  • Данные, созданные источником, считаются пустыми тогда и только тогда, когда они состоят исключительно из символов новой строки. (Это может быть на самом деле желательно.)
  • Данные, подаваемые в приемник, заканчиваются ровно одним символом новой строки, независимо от того, сколько новых строк заканчивается данными, созданными источником. (Это может быть проблемой.)

Опять же, все данные хранятся в памяти.

pipe_if_not_empty () {
  input=$(cat);
  if [ "x$input" != x"" ]; then
    { printf '%s\n' "${input}"; } | "$@"
  fi
}
4

Приведенная ниже функция пытается прочитать 1-й байт, и в случае успешного выполнения выводит этот байт, а остальные - кошки. Должен быть эффективным и 100% портативным.

if_read() {
    IFS="" read -rN 1 BYTE && { echo -nE "$BYTE"; cat; } | "$@";
}

Тестовые случаи:

$ echo -n | if_read wc -c
$ echo | if_read wc -c
1
$ echo -en "\nX" | if_read wc -c
2
$
2

По крайней мере, что-то вроде этого работает:

yourcommand | if [ $(wc -c) -gt "0" ]; then yourothercommand; fi

Обратите внимание, что приведенное выше будет рассматривать перевод строки и другие специальные символы в качестве вывода, поэтому пустая строка, переданная этому оператору if, будет считаться выводом. Просто увеличьте предел -gt, если ваш вывод обычно превышает 1 байт :)

2

Вместо sender | receiver:

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | receiver; fi; }
sender | tester

Или вы можете сделать его более универсальным, изменив его так, чтобы он принимал принимающую программу в качестве аргумента, как в ответе Жиля:

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | "$@"; fi; }
sender | tester receiver

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