Чи є спосіб передати висновок однієї функції до іншої, але тільки якщо є вихід?
Чи є спосіб передати висновок однієї функції до іншої, але тільки якщо є вихід?
Відповіді:
Наступна 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
}
Примітки дизайну:
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)
тому що якщо перший символ є новим рядком, заміна команд позбавить остаточного нового рядка, унеможливлюючи розрізнення порожнього вводу та введення, починаючи з порожнього рядка.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
}
Це має працювати для вас
$ --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.
Нижче наведена функція намагається прочитати 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
функція з прийнятої відповіді працює для мене навіть без нічого уникнути.
Принаймні щось подібне працює:
yourcommand | if [ $(wc -c) -gt "0" ]; then yourothercommand; fi
Зауважте, що вищевикладене буде розглядати канали рядків та інші спеціальні символи як вихід, тому порожній рядок передається до цього, якщо оператор буде розглянуто як вихід. Просто підніміть граничну величину -gt, якщо ваш вихідний звичайно повинен перевищувати 1 байт :)
yourothercommand
ніколи не бачить вихід yourcommand
.
Замість 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