Jak przesyłać dane wyjściowe z jednego procesu do drugiego, ale wykonywać je tylko wtedy, gdy pierwszy ma dane wyjściowe?

23

Jak mogę przepisać to polecenie na e-mail, tylko jeśli dane wyjściowe są wysyłane z mailq | grep?

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -s 'dd' email@email

Czy to w ogóle możliwe na jednej linii?

Zobacz Sprawdzanie, czy potok jest pusty i uruchom polecenie na danych, jeśli nie dotyczy to bardziej ogólnego przypadku niż wysyłanie wiadomości e-mail.

cosmin
źródło
Bardzo podobnie: uzależnij potok od niepustego zwrotu (w przypadku superużytkownika )
G-Man mówi „Przywróć Monikę”

Odpowiedzi:

19

Myślę, że będziesz musiał użyć pliku tymczasowego do tej operacji, abyś mógł użyć &&operatora do uruchomienia polecenia mail tylko wtedy, gdy grep zwrócił status wyjścia, który mówi, że ma takie dopasowania:

TMPFILE=`mktemp /tmp/mailqgrep.XXXXXX`; mailq | egrep 'rejected|refused' -A5 -B5 > "$TMPFILE" && mail -s 'dd' email@email < "$TMPFILE"; rm "$TMPFILE"

Jeśli nie przeszkadzało ci, że plik tymczasowy gdzieś się zatrzymał i możesz użyć jego statycznej nazwy, możesz pominąć specjalne czynności związane z nazywaniem i usuwaniem:

 mailq | egrep 'rejected|refused' -A5 -B5 > /tmp/mailqgrep && mail -s 'dd' email@email < /tmp/mailqgrep

Edycja: Po zobaczeniu odpowiedzi glenna bawiłem się tym trochę i najwyraźniej przypisanie zmiennej przy użyciu $()składni zwraca kod wyjścia polecenia, więc możesz pominąć test użyty dla długości łańcucha i użyć go zamiast tego. Oto wszystko w jednym poleceniu:

data=$(mailq | egrep 'rejected|refused' -A 5 -B 5) && mail -s 'dd' email@email <<< "$data"

Edycja 2: Po zobaczeniu odpowiedzi Simona sprawdziłem mój mailprogram. Nie zachowuje się w sposób opisany domyślnie, ale ma na to opcję. Ze strony podręcznika:

-EJeśli wiadomość wychodząca nie zawiera żadnego tekstu w pierwszej lub jedynej części wiadomości, nie wysyłaj jej, ale odrzuć w ciszy, skutecznie ustawiając zmienną skipemptybody przy uruchomieniu programu. Jest to przydatne do wysyłania wiadomości ze skryptów uruchomionych przez cron (8).

Umożliwiając to:

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -E -s 'dd' email@email
Caleb
źródło
1
siła współpracy !!
glenn jackman
3
Sugerowałbym, że odpowiedź ta mogłaby zostać ulepszona poprzez przeniesienie najlepszego rozwiązania na samą górę.
Mark Booth
@Wildcard Ta edycja nie była konieczna, ponieważ parametry wejściowe mktempnie zwrócą czegoś, co wymaga cytowania.
Caleb
2
Wiem. Jesteś jednym z nielicznych, który stara się zrozumieć, że cytaty czasem konieczne (i celowo je pomija). Jednak wartością domyślną powinno być cytowanie, chyba że wyraźnie chcesz zadzwonić glob(split(var)). Nie potrzebujesz tutaj dzielenia słów ani rozszerzania globu plików, więc nie pytaj o nie. Ponadto musimy pokazać właściwą drogę na tej stronie .
Wildcard
@Wildcard Do przyjęcia. Zazwyczaj biegam zshi właściwie nie dostaję globowania ani dzielenia słów w takich przypadkach, chyba że o to poproszę, ale ze względu na odpowiedzi tutaj i nie wiedząc, że docelowe środowisko cytowane z zasady jest w porządku.
Caleb
11

Program narzędziowy ifne istnieje specjalnie w tym celu: aby uruchomić polecenie, jeśli standardowe wejście nie jest puste.

mailq | egrep 'rejected|refused' -A 5 -B 5 | ifne mail -s 'dd' email@email

Polecenie ifne jest częścią pakietu moreutils , rozliczanego jako „rosnąca kolekcja narzędzi uniksowych, o których nikt wcześniej nie pisał, gdy był Unix”.

Guy Hillyer
źródło
8

Możesz zapisać dane wyjściowe w zmiennej, a następnie wywołać polecenie mail, jeśli długość ciągu nie jest równa zero.

data=$(mailq | egrep 'rejected|refused' -A 5 -B 5)
[[ -n "$data" ]] && mail -s 'dd' email@email <<< "$data"

W jednym wierszu połącz dwa polecenia średnikiem.

Glenn Jackman
źródło
1
Propozycje użycia zmiennej zamiast pliku tymczasowego. To zmusiło mnie do zastanowienia się, dokąd idzie kod wyjścia z polecenia, z którym wykonałeś przypisanie zmiennej, i najwyraźniej zostaje przekazany! Zobacz moją zaktualizowaną odpowiedź .
Caleb
7

Użyj mailz -Eopcją. Na moim Macu MAIL (1) mówi, że -Eflaga nie wyśle ​​e-maila z pustym ciałem.

-E Nie wysyłaj wiadomości z pustym ciałem. Jest to przydatne w przypadku błędów potoku ze skryptów cron (8).

Na komputerze Mac przeprowadziłem następujący test. Zauważ, że plik file_does_exististnieje, ale plik file_does__not_existnie istnieje.

To wysyła do mnie e-mail:

$ ls file_does_exist | egrep 'file' -A5 -B5 | mailx -E -s 'test' stefan@example.org

To nie dotyczy Zauważ, że polecenie ls file_does_not_exist | egrep ...nie generuje żadnych danych wyjściowych.

$ ls file_does_not_exist | egrep 'file' -A5 -B5 | mailx -E -s 'test' stefan@example.org
ls: file_does_not_exist: No such file or directory
Stefan Lasiewski
źródło
Caleb cię do tego pobił.
Gilles „SO- przestań być zły”
1
Pokonał mnie tylko 4,75 godziny :). Brakowało mi tego szczegółu w jego długiej odpowiedzi.
Stefan Lasiewski
5

W tym konkretnym przypadku, jeśli mailobsługujesz tę -Eopcję , po prostu jej użyj. W ogólnym przypadku możesz spróbować odczytać jedną postać; jeśli istnieje, uruchom komendę postprocessing i podaj ten znak z reszty pliku.

pipe_if_not_empty () {
  a=$(dd bs=1 count=1 2>/dev/null; echo .)  # read at most one character
  if [ "$a" != "." ]; then                  # if there were two characters,
    { printf %s "${a%.}";                   # then output the first character
      cat; } |                              # and the rest of the input   
    "$@"                                    # into the specified program
  fi
}

mailq | egrep 'rejected|refused' -A 5 -B 5 |
pipe_if_not_empty mail -s 'dd' email@email

Zwróć uwagę na przydatne użycie cat. Dodanie echo .sprawia, że ​​ta funkcja działa, nawet jeśli pierwszym znakiem na wejściu jest nowa linia (pamiętaj, że $(…)konstrukcja usuwa końcowe znaki nowej linii).

W przypadku większości powłok (cokolwiek innego niż zsh, o ile wiem), jeśli plik zaczyna się od znaku zerowego, kod ten uwierzy, że jest pusty. Naprawianie pozostawione jako ćwiczenie dla czytelnika. (Wskazówka: użyj odw pierwszej podpowłoce i printfwydrukuj pierwszy bajt.) (Rozwiązanie: jak sprawdzić, czy potok jest pusty ) Możesz napotkać ten sam problem, jeśli plik zaczyna się bajtem, który nie jest prawidłowym znakiem w bieżącym widownia; łatwiej to naprawić, uruchamiając ten kod za pomocą LC_ALL=C.

Gilles „SO- przestań być zły”
źródło
+1 za śmieszne, ale być może przydatne w innym scenariuszu :) Z ciekawości, po co wstrzykiwać i testować kropkę zamiast testować pusty ciąg? Czy to nie stanowi problemu, jeśli dane wejściowe zaczynają się od kropki? Czy korzystanie z readpomocy może to uprościć?
Caleb
@Caleb: Tak, powinienem wspomnieć, że odpowiadałem na pytanie zawarte w tytule, a nie na konkretną sytuację. Zobacz moją edycję, aby uzyskać wyjaśnienie kropki. Nie sądzę, że można odróżnić pusty pierwszy wiersz od pustego pliku za pomocą readprzenośnego sh (może to być możliwe w bash, ksh lub zsh).
Gilles „SO- przestań być zły”
3

IIRC mailpolecenie BSD przerywa działanie, jeśli otrzyma pusty komunikat.

Simon Richter
źródło
1
Program pocztowy w moim systemie GNU Linux (Scheda mailx 12.4) nie zachowują się w ten sposób domyślnie, ale ma się -Eopcję, aby włączyć go .
Caleb
1

Ostatnio dowiedziałem się o nazwie polecenia, ifnektóra jest częścią moreutilspakietu. Gdzie możesz go użyć do rozwiązania tego konkretnego problemu.

ifne - Uruchom polecenie, jeśli standardowe wejście nie jest puste

przykład ze strony man,

EXAMPLE
       find . -name core | ifne mail -s "Core files found" root
Rabin
źródło
0

Możesz przepisać swoje polecenie za pomocą, test -s /dev/stdinaby sprawdzić, czy część ma dane wyjściowe mailq | grep.

- mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -s 'dd' email@email
+ mailq | egrep 'rejected|refused' -A 5 -B 5 | (test -s /dev/stdin && cat) | mail -s 'dd' email@email
trent55
źródło