Как получить PIPESTATUS и вывод в bash-скрипте


9

Я пытаюсь получить дату последнего изменения файла с помощью этой команды

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

TM_LOCAL имеет значение типа "2012-05-16 23:18" после выполнения этой строки

Я также хотел бы проверить PIPESTATUS, чтобы увидеть, если была ошибка. Например, если файл не существует, lsвозвращает 2. Но $?имеет значение 0, поскольку имеет возвращаемое значение awk.

Если я запускаю эту команду в одиночку, я могу проверить возвращаемое значение ls, посмотрев на ${PIPESTATUS[0]}

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

Но $PIPESTATUSне работает, как я ожидал, если я назначу вывод переменной, как в первом примере. В этом случае $PIPESTATUSмассив имеет только 1 элемент, который совпадает с$?

Итак, вопрос в том, как я могу получить оба $PIPESTATUSи назначить вывод переменной одновременно?

Ответы:


8

Вы могли бы сделать это:

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

Тогда $?будет код возврата от ls. Это не сработает, если вам нужен код возврата более чем из одной части канала (но вы можете разделить конвейер, если выходные данные не слишком велики, как здесь).

Вот довольно дорогой способ получения полного PIPESTATUSмассива и вывода. Не очень элегантно, но больше ничего не нашел

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[@]}"

Который дает:

Output:
a
b
c
Results:
0 1 42

Это работает в моем случае, но мне все еще интересно, есть ли способ получить полный массив pipestatus и вывод.
Мустафа Сердар Шанлы

3

Использование set -o pipefailв , bashчтобы получить код выхода самого правой ненулевого в централизованному последовательности команд , как $?. От man bash:

Если установлено, возвращаемое значение конвейера - это значение последней (самой правой) команды для выхода с ненулевым статусом или ноль, если все команды в конвейере завершаются успешно. Эта опция по умолчанию отключена.

Тогда вы можете просто получить доступ $?. Используйте, set +o pipefailчтобы отключить снова.


2

Я предполагаю, что проблема заключается в том, что PIPESTATUS полностью исчезает, как только вы выполняете команду. Вы можете получить полный массив PIPESTATUS в bash версии 2 или выше:

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

Тогда доступа ${status[0]}, ${status[1]}и т.д.


2

Основная проблема с «тем, что вы ожидаете», заключается в том, что команда в обратных кавычках выполняется в подоболочке; $PIPESTATUSсуществует там, и статус, возвращаемый из m, следует тем же правилам, что и при выполнении одного исполняемого файла (или сценария оболочки). Статус команды backquote является самым правым ( awk).

Чтобы реализовать то, что сказал @ Daniel Beck , установите pipefailпараметр в подоболочке следующим образом:

TM_LOCAL=`set -o pipefail; ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'` теперь статус, сохраненный в $?последующем, будет статусом ls(если не ноль).

Тем не менее, я думаю, что явный if [ -f ~/.vimrc ];... тест был бы более читабельным.

Вы не можете получить вывод в переменную и PIPESTATUSвернуть его без временного файла для первого или маршалинга последнего в строку.


0

Я хотел отправить электронное письмо от fron cron, только если статус выхода не был нулевым

Хитрость заключается в том, что для получения stdin для конца конвейера вам нужно поместить его в подоболочку - но это, кажется, скрывает значение PIPESTATUS ...

тестовый крон выплевывает некоторые выходные данные и выходит с 1 или 0 ..

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

ОБНОВЛЕНИЕ: PIPESTATUS не виден, пока команда конвейера не обработана


0

Один из вариантов - проверить наличие вашего файла, прежде чем получить время его изменения с помощью вызова stat. Поскольку statв метке времени возвращается немного больше, чем вы хотите, вы можете обрезать его, используя расширение параметра.

С GNU stat(например, в Linux) вы можете запустить:

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

В Mac OS X и других системах BSD statсинтаксис отличается и может указывать формат времени:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M' ~/.vimrc 2>/dev/null)

В том, что сейчас является ответом GNU, вы говорите, что изменение $TM_LOCALбезопасно. Это безопасно, только если вы ожидали, что оно не будет иметь предыдущей ценности. Скажите, что значение было ранее, 2020-02-27 17:14и нет ~/.vimrcфайла. Вы бы тогда 2020-02-27 17. Поэтому я бы связал эти две строки вместе с дополнительным &&или (желательно, так как это не так легко читается) ifстрофой.
Адам Кац
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.