Jak uzyskać zarówno PIPESTATUS, jak i dane wyjściowe w skrypcie bash

9

Próbuję uzyskać datę ostatniej modyfikacji pliku za pomocą tego polecenia

TM_LOCAL=`ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'`

TM_LOCAL ma wartość „2012-05-16 23:18” po wykonaniu tego wiersza

Chciałbym również sprawdzić PIPESTATUS, aby zobaczyć, czy wystąpił błąd. Na przykład, jeśli plik nie istnieje, lszwraca 2. Ale $?ma wartość 0, ponieważ ma wartość zwracaną awk.

Jeśli uruchomię to polecenie sam, mogę sprawdzić zwracaną wartość ls, patrząc na ${PIPESTATUS[0]}

ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'

Ale $PIPESTATUSnie działa tak, jak się spodziewałem, jeśli przypiszę wynik do zmiennej, tak jak w pierwszym przykładzie. W tym przypadku $PIPESTATUStablica ma tylko 1 element, który jest taki sam jak$?

Pytanie brzmi: w jaki sposób mogę uzyskać oba $PIPESTATUSi jednocześnie przypisać dane wyjściowe do zmiennej?

Mustafa Serdar Şanlı
źródło

Odpowiedzi:

8

Możesz to zrobić:

TM_LOCAL=$(ls -l --time-style=long-iso ~/.vimrc | \
             awk '{ print $6" "$7 }' ; exit ${PIPESTATUS[0]} )

Wtedy $?będzie kod powrotu z ls. Nie działa to, jeśli potrzebujesz kodu powrotu z więcej niż jednej części potoku (ale możesz podzielić potok, jeśli wynik nie jest zbyt duży, tak jak tutaj).

Oto dość drogi sposób na uzyskanie pełnej PIPESTATUStablicy i wyniku. Niezbyt elegancki, ale nie znalazłem nic więcej:

result=$(echo -e "a\nb\nc" | \
          ( cat ; exit 1 ) | \
          ( cat ; exit 42 ) ; echo ${PIPESTATUS[@]})
output=$(head -n -1 <<< "$result")
status=($(tail -n 1 <<< "$result"))
echo "Output:"
echo "$output"
echo "Results:"
echo "${status[@]}"

Co daje:

Output:
a
b
c
Results:
0 1 42
Mata
źródło
Działa to w moim przypadku, ale nadal jestem ciekawy, czy istnieje sposób na uzyskanie pełnej tablicy pipestatus i wyniku.
Mustafa Serdar Şanlı
3

Użyj set -o pipefailw, bashaby uzyskać najbardziej niezerowy kod wyjścia w potokowej sekwencji poleceń jako $?. Od man bash:

Jeśli jest ustawiona, zwracana wartość potoku jest wartością ostatniego (najbardziej po prawej) polecenia, aby wyjść ze statusem niezerowym lub zero, jeśli wszystkie polecenia w potoku zakończą się pomyślnie. Ta opcja jest domyślnie wyłączona.

Następnie możesz po prostu uzyskać dostęp $?. Użyj, set +o pipefailaby wyłączyć ponownie.

Daniel Beck
źródło
2

Zakładam, że problemem jest to, że PIPESTATUS znika w całości, jak tylko wykonasz polecenie. Możesz uzyskać pełną tablicę PIPESTATUS w wersji bash 2 lub wyższej w ten sposób:

declare -a status
status=(${PIPESTATUS[@]})

Następnie dostępu ${status[0]}, ${status[1]}itp

eewanco
źródło
2

Główny problem z „tym, czego oczekujesz” polega na tym, że polecenie w cudzysłowach jest wykonywane w podpowłoce; $PIPESTATUSistnieje tam, a status zwrócony z m zachowuje te same zasady, jak w przypadku wykonania jednego pliku wykonywalnego (lub skryptu powłoki). Status komendy backquote to status skrajnie prawy ( awk).

Aby wdrożyć to, co powiedział @ Daniel Beck , ustaw odpowiednio pipefailopcję w podpowłoce:

TM_LOCAL=`set -o pipefail; ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'` teraz status zapisany $?później będzie miał status ls(jeśli niezerowy).

Myślę jednak, że wyraźny if [ -f ~/.vimrc ];... test byłby bardziej czytelny.

Nie można uzyskać danych wyjściowych do zmiennej i PIPESTATUSzwracanych bez pliku tymczasowego dla pierwszego, lub łączenia drugiego w ciąg.

toddkaufmann
źródło
0

Chciałem wysłać e-mail z cron tylko wtedy, gdy status wyjścia nie był równy zero

Sztuczka polega na tym, że aby uzyskać standardowe wejście na koniec potoku, należy umieścić go w podpowłoce - ale wydaje się, że to ukrywa wartość PIPESTATUS ...

test cron wyrzuca część danych wyjściowych i wychodzi z 1 lub 0 ..

./testcron | (test ${PIPESTATUS[0]} -ne 0 || mail -s "testcron output" paul)

AKTUALIZACJA: PIPESTATUS nie jest widoczny, dopóki polecenie potoku nie zostanie przetworzone

Paul Davey
źródło
0

Jedną z opcji jest sprawdzenie istnienia pliku przed uzyskaniem czasu modyfikacji z wywołaniem stat. Ponieważ statzwraca znacznik czasu nieco więcej niż chcesz, możesz go przyciąć za pomocą rozszerzenia parametrów.

W GNU stat(np. W systemie Linux) możesz uruchomić:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -c '%y' ~/.vimrc 2>/dev/null)
TM_LOCAL=${TM_LOCAL%:*}  # Safe to do, even if stat fails

W Mac OS X i innych systemach BSD statskładnia jest różna i może określać format czasu:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M' ~/.vimrc 2>/dev/null)
chepner
źródło
W tym, co jest teraz odpowiedzią GNU, mówisz, że zmiana na $TM_LOCALjest bezpieczna. Jest bezpieczny tylko wtedy, gdy spodziewałeś się, że nie będzie miał wcześniejszej wartości. Powiedzmy, że wartość była wcześniej 2020-02-27 17:14i nie ma ~/.vimrcpliku. Miałbyś wtedy 2020-02-27 17. Dlatego połączę te dwie linie razem z dodatkową &&lub (najlepiej, ponieważ nie jest to tak czytelne) użyj ifzwrotki.
Adam Katz