次のスクリプトを作成しました。
# !/bin/bash
# OUTPUT-COLORING
red='\e[0;31m'
green='\e[0;32m'
NC='\e[0m' # No Color
# FUNCTIONS
# directoryExists - Does the directory exist?
function directoryExists {
cd $1
if [ $? = 0 ]
then
echo -e "${green}$1${NC}"
else
echo -e "${red}$1${NC}"
fi
}
# EXE
directoryExists "~/foobar"
directoryExists "/www/html/drupal"
スクリプトは機能しますが、私のエコーの横に、次の場合の出力もあります。
cd $1
実行に失敗します。
testscripts//test_labo3: line 11: cd: ~/foobar: No such file or directory
これを捕まえることはできますか?
スクリプトは実行時にディレクトリを変更します。つまり、一連の相対パス名では機能しません。次に、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
を使用するように編集されています。)
エラー終了モードを設定するには、_set -e
_を使用します。単純なコマンドがゼロ以外のステータス(失敗を示す)を返した場合、シェルは終了します。
_set -e
_が常に有効であるとは限らないことに注意してください。テスト位置のコマンドは失敗することが許可されています(例:_if failing_command
_、_failing_command || fallback
_)。サブシェルのコマンドは、親ではなくサブシェルを終了するだけです:set -e; (false); echo foo
はfoo
を表示します。
別の方法として、またはそれに加えて、bash(およびkshとzshですが、プレーンなshではありません)では、ERR
トラップを使用して、コマンドがゼロ以外のステータスを返した場合に実行されるコマンドを指定できます。 _trap 'err=$?; echo >&2 "Exiting on error $err"; exit $err' ERR
_。 _(false); …
_のような場合、ERRトラップはサブシェルで実行されるため、親を終了させることはできません。
@ Gilles 'answer を拡張するには:
実際、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
このアイデアに基づいて作成された一般的な関数を次に示します。 local
キーワードを削除すると、すべてのPOSIX互換シェルで機能します。つまり、すべての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
に渡すコマンドから実行されるシェル変数のすべての変更が呼び出し元の関数に伝達されないことです。
あなたは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
、しかしこれはあなたが将来デバッグを難しくするでしょう。次の場所でifテストフラグを確認できます ドキュメントの場合はbash :