Зробіть трубу умовною при непорожньому поверненні


16

Відповіді:


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/nullLinux.
  • З іншого боку, head -n 1це не підходить, оскільки headзазвичай буферизує його вхід і може прочитати більше, ніж один рядок, який він виводить - а оскільки він читає з труби, зайві байти просто втрачаються.
  • read -r headі навіть read -r -n 1 headтут не підходять, тому що якщо перший символ є новим рядком, headвін буде встановлений у порожній рядок, що робить неможливим розрізнити порожній вхід і вхід, починаючи з порожнього рядка.
  • Ми не можемо просто писати, head=$(head -c 1)тому що якщо перший символ є новим рядком, заміна команд позбавить остаточного нового рядка, унеможливлюючи розрізнення порожнього вводу та введення, починаючи з порожнього рядка.
  • У БАШЕЄВ, 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
}

17

Це має працювати для вас

$ --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.


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
$

Ця функція для мене відмовляється під час запуску echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" foo@bar.com. Він вважає , що lineі hereобидва одержувачі електронної пошти, а НЕ токени в темі. Мені потрібно уникнути "навколишнього предмета, щоб змусити його працювати. Однак pipe_if_not_emptyфункція з прийнятої відповіді працює для мене навіть без нічого уникнути.
kuzzooroo

2

Принаймні щось подібне працює:

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

Зауважте, що вищевикладене буде розглядати канали рядків та інші спеціальні символи як вихід, тому порожній рядок передається до цього, якщо оператор буде розглянуто як вихід. Просто підніміть граничну величину -gt, якщо ваш вихідний звичайно повинен перевищувати 1 байт :)


yourothercommandніколи не бачить вихід yourcommand.
Призупинено до подальшого повідомлення.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.