Проверьте, содержит ли ошибка команды подстроку
У меня много команд bash. Некоторые из них терпят неудачу по разным причинам. Я хочу проверить, содержат ли некоторые из моих ошибок подстроку.
Вот пример:
#!/bin/bash
if [[ $(cp nosuchfile /foobar) =~ "No such file" ]]; then
echo "File does not exist. Please check your files and try again."
else
echo "No match"
fi
Когда я запускаю его, ошибка выводится на экран, и я получаю "Нет совпадения":
$ ./myscript
cp: cannot stat 'nosuchfile': No such file or directory
No match
Вместо этого я хотел, чтобы ошибка была зафиксирована и соответствовала моему состоянию:
$ ./myscript
File does not exist. Please check your files and try again.
Как правильно сопоставить сообщение об ошибке?
PS Я нашел какое-то решение, что вы думаете об этом?
out=`cp file1 file2 2>&1`
if [[ $out =~ "No such file" ]]; then
echo "File does not exist. Please check your files and try again."
elif [[ $out =~ "omitting directory" ]]; then
echo "You have specified a directory instead of a file"
fi
2 ответа
Я бы сделал это так
# Make sure we always get error messages in the same language
# regardless of what the user has specified.
export LC_ALL=C
case $(cp file1 file2 2>&1) in
#or use backticks; double quoting the case argument is not necessary
#but you can do it if you wish
#(it won't get split or glob-expanded in either case)
*"No such file"*)
echo >&2 "File does not exist. Please check your files and try again."
;;
*"omitting directory"*)
echo >&2 "You have specified a directory instead of a file"
;;
esac
Это также будет работать с любой оболочкой POSIX, что может пригодиться, если вы когда-нибудь решите преобразовать свои сценарии bash в оболочку POSIX (dash
немного быстрее, чем bash
).
Вам нужен первый 2>&1
перенаправление, потому что исполняемые файлы обычно выводят информацию, не предназначенную для дальнейшей машинной обработки, в stderr
, Вы должны использовать >&2
перенаправления с echo
потому что то, что вы там выводите, вписывается в эту категорию.
Ответ PSkocik является правильным, если вам нужно проверить конкретную строку в сообщении об ошибке. Однако, если вы пришли к поиску способов обнаружения ошибок:
Я хочу проверить, была ли сбой команды
Проверьте код выхода вместо сообщений об ошибках:
if cp nosuchfile /foobar
then
echo "The copy was successful."
else
ret="$?"
echo "The copy failed with exit code $ret"
fi
Я хочу различать различные виды неудач
Перед поиском подстрок проверьте документацию кода выхода для вашей команды. Например, man wget
списки:
EXIT STATUS
Wget may return one of several error codes if it encounters problems.
0 No problems occurred.
1 Generic error code.
2 Parse error---for instance, when parsing command-line options
3 File I/O error.
(...)
в этом случае вы можете проверить это напрямую:
wget "$url"
case "$?" in
0) echo "No problem!";;
6) echo "Incorrect password, try again";;
*) echo "Some other error occurred :(" ;;
esac
Не все команды являются дисциплинированными в своем состоянии выхода, поэтому вам может потребоваться вместо этого проверить подстроки.
Оба примера:
out=`cp file1 file2 2>&1`
а также
case $(cp file1 file2 2>&1) in
имеют ту же проблему, потому что они смешивают stderr и stdout в один вывод, который можно проверить. Проблема в том, что вы пытаетесь выполнить сложную команду с интерактивным выводом, т.е.top
или ddrescue
и вам нужно сохранить stdout нетронутым и исследовать только stderr. Чтобы опустить эту проблему, вы можете попробовать следующее (работает только в bash > v4.2!):
shopt -s lastpipe
declare errmsg_variable="errmsg_variable UNSET"
command 3>&1 1>&2 2>&3 | read errmsg_variable
if [[ "$errmsg_variable" == *"substring to find"* ]]; then
#commands to execute only when error occurs and specific substring find in stderr
fi
Объяснение
Эта линия
command 3>&1 1>&2 2>&3 | read errmsg_variable
перенаправление stderr на переменную errmsg_variable (используя трюк с файловыми дескрипторами и конвейер) без смешивания с stdout. Обычно каналы порождают собственные подпроцессы, и после выполнения команды с конвейерами все назначения не видны в основном процессе, поэтому их изучение в остальной части кода не может быть эффективным. Чтобы предотвратить это, вы должны изменить стандартное поведение оболочки, используя:
shopt -s lastpipe
который выполняет последнюю манипуляцию с каналом в команде, как в текущем процессе, так:
| read errmsg_variable
назначает содержимое, "закачанное" в канал (в нашем случае сообщение об ошибке), в переменную, которая находится в основном процессе. Теперь вы можете изучить эту переменную в остальной части кода, чтобы найти конкретную подстроку:
if [[ "$errmsg_variable" == *"substring to find"* ]]; then
#commands to execute only when error occurs and specific substring find in stderr
fi