shell scriptはtext fileを「どのinterpreterで」「どのcurrent directoryとenvironmentで」「どの権限で」実行するかによって結果が変わります。bash script.sh、./script.sh、source script.shは同じではありません。初めてのscriptは構文検査、無害なsample入力、終了status確認を通してから実行します。
結論は「Bash scriptは明示的なshebangを置き、bash -nで構文を確認してからbash ./script.shまたは実行権限付きの./script.shで起動します。sourceはcurrent shellを変更する必要があるtrusted fileだけに限定します」です。
三つの実行方法を混同しない
bash ./tool.shはBash programへfileを引数として渡すためfileのexecute bitは不要です。./tool.shはkernelがexecute permissionとshebangを確認します。source ./tool.shまたはdot builtinはcurrent shell contextでcommandを読み、variable、current directory、option、trapを呼び出し元へ残します。
- scriptのinterpreterとBash version
- script fileのowner・mode・hash
- current directory、PATH、locale、timezone
- 必要なargumentとinput fileのscope
- 成功・0件・errorのexit status定義
実行前にfile内容をreviewし、download元とhashを確認します。sudo、curl pipe shell、credential、delete、network送信が含まれるscriptはtest環境でもowner承認なしに動かしません。line endingがCRLFだとshebang interpreter名へCRが混ざる場合があるためfileやod等で確認します。
対象スクリプトの先頭行、改行コード、実行権限、所有者、保存先、想定shell、引数を実行前に読みます。file、head、stat、bash -nを使い、Bash構文をshで起動していないか、Windows由来のCRLFでshebangが解釈できないかを確認します。カレントディレクトリに同名コマンドがあってもPATH検索へ頼らず、./script.shまたは絶対パスで対象を明示します。
最小scriptを構文検査して動かす
scriptへ#!/usr/bin/env bashまたは運用で固定したBash pathを選び、set optionは目的を理解して記述します。bash -nでparseし、test directoryとsample argumentで一回実行します。stdout、stderr、status、作成fileを確認してから本番scopeを一段階だけ広げます。
最小のBash script
#!/usr/bin/env bash
printf 'script=%s argc=%s\n' "$0" "$#"
printf 'first=%s\n' "$1"
argumentが必須なら先に件数を検証し、unquoted expansionを避けます。
構文だけを確認
bash -n -- ./tool.sh
commandを実行せずparse errorを確認しますが、logic errorやruntime failureは検出しません。
Bashへ明示的に渡す
bash -- ./tool.sh sample-input
execute bitがなくてもBashがreadできれば実行され、shebangより指定したBashが優先されます。
executableとして起動
./tool.sh sample-input
rc=$?
printf 'exit=%s\n' "$rc"
current directoryはPATH検索されないため./を付け、statusを直後に保存します。
subshellで環境影響を隔離
( cd ./test-fixture && ../tool.sh sample-input )
printf 'parent_dir=%s\n' "$PWD"
parent shellのcurrent directoryを変えないtestです。file変更等の外部影響はsubshellでも残ります。
動作確認は副作用のない小さなfixtureとtest directoryで行います。Bash用ならbash — ./script.sh argのようにinterpreterを明示する方法、実行権限と正しいshebangがあるなら./script.shを使う方法を分けます。source ./script.shは現在のshellへ変数、関数、working directory、optionを持ち込むため、環境設定用と確認できるスクリプト以外には使いません。
shebangとcommand検索の仕組み
shebangはexecutable fileをどのinterpreterへ渡すかを示します。/usr/bin/env bashはPATHからBashを探すためportableな場合がある一方、PATHが不正なら別実体を選びます。absolute path固定にもdistribution差があります。実行statusは最後のcommandまたは明示exitで決まり、stdout表示だけでは成功を判断できません。
- bash script.shは指定Bashでfileを読みexecute bitを必須としない
- ./script.shはexecute permissionとshebangが必要である
- sourceはcurrent shellのvariable、directory、optionを変更し得る
- bash -nはsyntaxを検査するがcommand runtimeはtestしない
- script名だけの実行はPATH検索に依存しcurrent directoryを自動検索しない
bash script.shは新しいBash processがファイルを読み、./script.shはkernelがshebangのinterpreterへ渡します。sourceは新processを作らず現在のshellで実行するため、exit、cd、set、trap、readonlyの影響が呼び出し元へ残ります。位置引数、環境変数、標準入力、exit statusも呼び出し方式で扱いが変わるため、単に「動けば同じ」とみなしません。
untrusted scriptをsourceしない
third-party scriptをsource、sudo、root shellで試しません。production pathをsampleにせず、read-only fixtureと専用userを使います。scriptが作るfileのowner、mode、保存先を確認し、secretをargumentやxtraceへ出しません。変更操作にはbackupとrollbackを別に用意します。
- bash実行とsourceを同じ効果と思う
- current directoryをPATHへ無条件に追加する
- bash -n成功を安全性確認とする
- unquoted positional parameterでargument境界を壊す
- scriptのexit statusを後続commandで上書きする
インターネットやメールから得たスクリプトを内容確認なしに実行・sourceしません。root権限、パッケージ変更、ファイル削除、ネットワーク送信、credential参照、cron登録が含まれないかを読みます。sudo bash scriptのように全体を高権限で動かす前に、必要操作を分離します。環境変数へ秘密を置く場合はxtraceやprocess環境からの露出を考え、テストでは模擬値を使います。
引数・environment・終了statusを検証する
空argument、spaceを含むargument、存在しないinput、permission不足、command不存在をtestします。stdout/stderr/status、作成file、parent directory、environment変化を記録し、同じBash versionとfixtureで再現できることを確認します。
- interpreter・script hash・mode・実行userを記録した
- bash -nとsample実行を通過した
- sourceを使わずparent shellへの副作用を制御した
- 成功・0件・errorのstatusを期待表で確認した
正常引数、引数なし、無効値、空文字、スペースを含む値、途中command失敗、signal、中断後cleanupをfixtureで確認します。stdout、stderr、exit status、作成・変更されたファイル、working directory、残存processを記録します。sourceを使う設定スクリプトなら、実行前後のset -o、trap、変数、関数、pwdを比較し、呼び出しshellを意図せず汚していないか確認します。
同じ条件で再実行できる方法を選ぶ
current shellへaliasやenvironmentを読み込む明確な目的がなければsourceを使いません。複数hostで動かすならBash version、dependency、PATHを仕様化します。root権限が必要と書かれたscriptは、必要な一commandへ権限を絞れるかreviewします。
一度だけBashスクリプトを動かすならbash — scriptで十分で、配布する実行物ならshebang、実行権限、依存command、usage、exit codeを整えます。現在shellへ設定を読み込む用途だけsourceを選びます。複雑なdata処理、並列実行、厳格な型、外部入力が多い処理は、Bashへ機能を足し続けずPython等の適切な言語とtest frameworkへ移す判断をします。

コメント