--
является наиболее часто используемым в команде для указания на конец опций. Это полезно, если Ваше имя файла начинается "-", или Ваш вход неизвестен. Вот пример его использования:
git diff --stat -- --file1 --file2
--file1
рассматривается как имя файла, а не другую опцию.
Ваши каталоги изменений сценария когда это работает, что означает это, не будут работать с серией относительных путей. Вы затем прокомментировали позже, что только хотели проверить на существование каталога, не способность использовать cd
, таким образом, ответы не должны использовать cd
вообще. Пересмотренный. Используя tput
и цвета от man terminfo
:
#!/bin/bash -u
# OUTPUT-COLORING
red=$( tput setaf 1 )
green=$( tput setaf 2 )
NC=$( tput setaf 0 ) # or perhaps: tput sgr0
# FUNCTIONS
# directoryExists - Does the directory exist?
function directoryExists {
# was: do the cd in a sub-shell so it doesn't change our own PWD
# was: if errmsg=$( cd -- "$1" 2>&1 ) ; then
if [ -d "$1" ] ; then
# was: echo "${green}$1${NC}"
printf "%s\n" "${green}$1${NC}"
else
# was: echo "${red}$1${NC}"
printf "%s\n" "${red}$1${NC}"
# was: optional: printf "%s\n" "${red}$1 -- $errmsg${NC}"
fi
}
(Отредактированный для использования более неуязвимого printf
вместо проблематичного echo
это могло бы действовать на escape-последовательности в тексте.)
Использовать set -e
установить режим выхода на ошибке: если простая команда возвращает ненулевое состояние (указание на отказ), выходы оболочки.
Остерегайтесь этого set -e
не всегда умирает. Командам в тестовых положениях позволяют перестать работать (например. if failing_command
, failing_command || fallback
). Команды в подоболочке только приводят к выходу из подоболочки, не родителя: set -e; (false); echo foo
дисплеи foo
.
С другой стороны, или кроме того, в ударе (и ksh и zsh, но не простой sh), можно указать команду, это выполняется в случае, если команда возвращает ненулевое состояние, с ERR
прерывание, например. trap 'err=$?; echo >&2 "Exiting on error $err"; exit $err' ERR
. Отметьте это в случаях как (false); …
, ДОПУСКАТЬ ОШИБКУ прерывание выполняется в подоболочке, таким образом, это не может заставить родителя выходить.
||
поведение, которое включает, чтобы легко сделать надлежащую обработку ошибок, не используя прерывания. См. мой ответ. Что Вы думаете о том методе? попытка
– skozin
11.01.2016, 18:36
ERR
псевдосигнал поддерживается во всех главных оболочках. Спасибо за обзор! =)
– skozin
11.01.2016, 19:20
Вы не говорите, под чем точно Вы подразумеваете catch
---сообщите и продолжите; прервать последующую обработку?
С тех пор cd
возвращает ненулевое состояние при отказе, Вы могли сделать:
cd -- "$1" && echo OK || echo NOT_OK
Вы могли просто выйти при отказе:
cd -- "$1" || exit 1
Или, повторите свое собственное сообщение и выход:
cd -- "$1" || { echo NOT_OK; exit 1; }
И/или подавите ошибку, обеспеченную cd
при отказе:
cd -- "$1" 2>/dev/null || exit 1
По стандартам команды должны поместить сообщения об ошибках на STDERR (дескриптор файла 2). Таким образом 2>/dev/null
говорит перенаправление STDERR "битоприемнику", известному /dev/null
.
(не забывайте заключать свои переменные в кавычки и отмечать конец опций для cd
).
На самом деле для Вашего случая я сказал бы, что логика может быть улучшена.
Вместо CD и затем проверяют, существует ли он, проверьте, существует ли он, затем входят в каталог.
if [ -d "$1" ]
then
printf "${green}${NC}\\n" "$1"
cd -- "$1"
else
printf "${red}${NC}\\n" "$1"
fi
Но если Ваша цель состоит в том, чтобы заставить возможные ошибки замолчать затем cd -- "$1" 2>/dev/null
, но это заставит Вас отладить в будущем тяжелее. Можно проверить при тестировании флагов в: Bash, если документация:
$1
переменная и перестанет работать, если та переменная будет содержать пробелы или другие метасимволы оболочки. Этому также не удается проверить, есть ли у пользователя разрешение к cd
в него.
– Ian D. Allen
22.10.2013, 16:38
В развитие ответа @Gilles:
Действительно, set -e
не работает внутри команд, если после них используется оператор ||
, даже если вы запускаете их в подшивке; например, это не сработает:
#!/bin/sh
# prints:
#
# --> outer
# --> inner
# ./so_1.sh: line 16: some_failed_command: command not found
# <-- inner
# <-- outer
set -e
outer() {
echo '--> outer'
(inner) || {
exit_code=$?
echo '--> cleanup'
return $exit_code
}
echo '<-- outer'
}
inner() {
set -e
echo '--> inner'
some_failed_command
echo '<-- inner'
}
outer
Но оператор ||
необходим для предотвращения возврата из внешней функции перед очисткой.
Есть небольшой трюк, который можно использовать, чтобы исправить это: запустите внутреннюю команду в фоновом режиме, а затем немедленно дождитесь ее выполнения. Встроенная функция wait
вернет код выхода внутренней команды, и теперь вы используете ||
после wait
, а не внутреннюю функцию, поэтому set -e
корректно работает внутри последней:
#!/bin/sh
# prints:
#
# --> outer
# --> inner
# ./so_2.sh: line 27: some_failed_command: command not found
# --> cleanup
set -e
outer() {
echo '--> outer'
inner &
wait $! || {
exit_code=$?
echo '--> cleanup'
return $exit_code
}
echo '<-- outer'
}
inner() {
set -e
echo '--> inner'
some_failed_command
echo '<-- inner'
}
outer
Вот общая функция, основанная на этой идее. Она должна работать во всех POSIX-совместимых оболочках, если вы удалите ключевые слова local
, т.е. заменить все local x=y
на x=y
:
# [CLEANUP=cleanup_cmd] run cmd [args...]
#
# `cmd` and `args...` A command to run and its arguments.
#
# `cleanup_cmd` A command that is called after cmd has exited,
# and gets passed the same arguments as cmd. Additionally, the
# following environment variables are available to that command:
#
# - `RUN_CMD` contains the `cmd` that was passed to `run`;
# - `RUN_EXIT_CODE` contains the exit code of the command.
#
# If `cleanup_cmd` is set, `run` will return the exit code of that
# command. Otherwise, it will return the exit code of `cmd`.
#
run() {
local cmd="$1"; shift
local exit_code=0
local e_was_set=1; if ! is_shell_attribute_set e; then
set -e
e_was_set=0
fi
"$cmd" "$@" &
wait $! || {
exit_code=$?
}
if [ "$e_was_set" = 0 ] && is_shell_attribute_set e; then
set +e
fi
if [ -n "$CLEANUP" ]; then
RUN_CMD="$cmd" RUN_EXIT_CODE="$exit_code" "$CLEANUP" "$@"
return $?
fi
return $exit_code
}
is_shell_attribute_set() { # attribute, like "x"
case "$-" in
*"$1"*) return 0 ;;
*) return 1 ;;
esac
}
Пример использования:
#!/bin/sh
set -e
# Source the file with the definition of `run` (previous code snippet).
# Alternatively, you may paste that code directly here and comment the next line.
. ./utils.sh
main() {
echo "--> main: $@"
CLEANUP=cleanup run inner "$@"
echo "<-- main"
}
inner() {
echo "--> inner: $@"
sleep 0.5; if [ "$1" = 'fail' ]; then
oh_my_god_look_at_this
fi
echo "<-- inner"
}
cleanup() {
echo "--> cleanup: $@"
echo " RUN_CMD = '$RUN_CMD'"
echo " RUN_EXIT_CODE = $RUN_EXIT_CODE"
sleep 0.3
echo '<-- cleanup'
return $RUN_EXIT_CODE
}
main "$@"
Выполнение примера:
$ ./so_3 fail; echo "exit code: $?"
--> main: fail
--> inner: fail
./so_3: line 15: oh_my_god_look_at_this: command not found
--> cleanup: fail
RUN_CMD = 'inner'
RUN_EXIT_CODE = 127
<-- cleanup
exit code: 127
$ ./so_3 pass; echo "exit code: $?"
--> main: pass
--> inner: pass
<-- inner
--> cleanup: pass
RUN_CMD = 'inner'
RUN_EXIT_CODE = 0
<-- cleanup
<-- main
exit code: 0
Единственное, о чем вам нужно знать при использовании этого метода, это то, что все изменения переменных шелла, сделанные из команды, которую вы передаете в run
, не распространятся на вызывающую функцию, потому что команда выполняется в подшелле.