Проверьте, содержит ли ошибка команды подстроку

У меня много команд 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
Другие вопросы по тегам