Uzupełnij potok pod warunkiem niepustego powrotu

Odpowiedzi:

9

Poniższa ifnotemptyfunkcja potokuje dane wejściowe do polecenia przekazanego jako argument, z wyjątkiem tego, że nic nie robi, jeśli dane wejściowe są puste. Użyj go do rury source --foopod sink --barpisząc 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
}

Uwagi do projektu:

  • Spodziewałbym się, że ta implementacja będzie działać na wszystkich platformach POSIX / Unix, choć ściśle mówiąc, nie jest zgodna ze standardami: polega na tym, że ddnie czyta więcej niż jeden bajt, który czytał na swoim standardowym wejściu.
  • Myślę, że head -c 1byłby odpowiednim zamiennikiem dla dd bs=1 count=1 2>/dev/nullsystemu Linux.
  • Z drugiej strony head -n 1nie byłby odpowiedni, ponieważ headzwykle buforuje jego dane wejściowe i może czytać więcej niż jedną linię, którą wysyła - a ponieważ odczytuje z potoku, dodatkowe bajty są po prostu tracone.
  • read -r heada nawet read -r -n 1 headnie są tu odpowiednie, ponieważ jeśli pierwszy znak jest znakiem nowej linii, headzostanie ustawiony na pusty ciąg znaków, co uniemożliwi rozróżnienie między pustym wejściem a wejściem zaczynającym się od pustej linii.
  • Nie możemy po prostu pisać, head=$(head -c 1)ponieważ jeśli pierwszy znak jest znakiem nowej linii, podstawienie polecenia usunie ostatnią linię, uniemożliwiając rozróżnienie między pustym wejściem a wejściem zaczynającym się od pustej linii.
  • W bash, ksh lub zsh, można zastąpić catprzez </dev/stdindo mikroskopijnej przyrostu wydajności.

Jeśli nie masz nic przeciwko przechowywaniu w pamięci całych danych pośrednich, oto bardzo nieznacznie prostsza implementacja pipe_if_not_empty.

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

Oto nieco prostsza implementacja z następującymi zastrzeżeniami:

  • Dane wytworzone przez źródło są uważane za puste wtedy i tylko wtedy, gdy składają się wyłącznie ze znaków nowej linii. (To może być w rzeczywistości pożądane.)
  • Dane wprowadzane do ujścia kończą się dokładnie jednym znakiem nowego wiersza, bez względu na to, ile nowych wierszy kończy dane generowane przez źródło. (To może być problem.)

Ponownie całe dane są przechowywane w pamięci.

pipe_if_not_empty () {
  input=$(cat);
  if [ "x$input" != x"" ]; then
    { printf '%s\n' "${input}"; } | "$@"
  fi
}
Gilles „SO- przestań być zły”
źródło
17

To powinno ci pomóc

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

Przykład

$ 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

To proste, ale powinno działać dla Ciebie. Jeśli twoja „funkcja” wysyła pusty ciąg lub nawet nowy wiersz w dół rurociągu, xargs -r uniemożliwi przejście do „innej funkcji”.

Dokumentacja 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.
Randy Skretka
źródło
9

ifne (1) z moreutils robi dokładnie to. Moreutils jest dostępny jako pakiet przynajmniej w Debianie i Ubuntu, prawdopodobnie także w innych dystrybucjach.

Jukka Matilainen
źródło
4

Poniższa funkcja próbuje odczytać pierwszy bajt, a jeśli pomyślne echo tego bajtu, a pozostałe koty. Powinien być wydajny i w 100% przenośny.

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

Przypadki testowe:

$ echo -n | if_read wc -c
$ echo | if_read wc -c
1
$ echo -en "\nX" | if_read wc -c
2
$
Benjamin Polak
źródło
Ta funkcja nie działa dla mnie po uruchomieniu echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" [email protected]. Uważa, że linei hereoboje są adresatami wiadomości e-mail, a nie tokenami w temacie. Muszę uciec od "otaczającego tematu, aby go uruchomić. Jednak pipe_if_not_emptyfunkcja z zaakceptowanej odpowiedzi działa dla mnie nawet bez niczego.
kuzzooroo,
2

Przynajmniej coś takiego działa:

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

Należy pamiętać, że powyższe rozważa wstawianie wierszy i inne znaki specjalne jako dane wyjściowe, więc pusty wiersz jest przekazywany do tego, jeśli instrukcja będzie uważana za wynik. Po prostu podnieś limit -gt, jeśli twój wynik powinien zwykle przekraczać 1 bajt :)

Janne Pikkarainen
źródło
yourothercommandnigdy nie widzi wyniku yourcommand.
Wstrzymano do odwołania.
2

Zamiast sender | receiver:

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

Możesz też uczynić to bardziej ogólnym celem, zmieniając go tak, aby akceptował program odbierający jako argument jak w odpowiedzi Gillesa:

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | "$@"; fi; }
sender | tester receiver
Wstrzymano do odwołania.
źródło