Norman の回答を 6 行に拡張しますが、最後の行は空白です。
#!/bin/ksh
#
# @(#)$Id$
#
# Purpose
3 行目はバージョン管理識別文字列です。実際には@(#)
、(SCCS) プログラムによって識別できる SCCS マーカー ' ' とwhat
、ファイルがデフォルトの VCS である RCS の下に置かれたときに展開される RCS バージョン文字列とのハイブリッドです。私的用途で使用しています。RCS プログラムは、 のように見えるident
拡張形式の を取得します。5 行目で、スクリプトの先頭にその目的の説明が必要であることを思い出しました。単語をスクリプトの実際の説明に置き換えます (たとえば、後にコロンがないのはそのためです)。$Id$
$Id: mkscript.sh,v 2.3 2005/05/20 21:06:35 jleffler Exp $
その後、シェルスクリプトの標準は本質的に何もありません。表示される標準フラグメントはありますが、すべてのスクリプトに表示される標準フラグメントはありません。(私の議論は、スクリプトが Bourne、Korn、または POSIX (Bash) シェル表記法で書かれていることを前提としています。シジルの後に C シェルの派生物を置く人がなぜ#!
罪に生きているのかについては、まったく別の議論があります。)
たとえば、次のコードは、スクリプトが中間 (一時) ファイルを作成するたびに、何らかの形で表示されます。
tmp=${TMPDIR:-/tmp}/prog.$$
trap "rm -f $tmp.?; exit 1" 0 1 2 3 13 15
...real work that creates temp files $tmp.1, $tmp.2, ...
rm -f $tmp.?
trap 0
exit 0
最初の行は一時ディレクトリを選択します。ユーザーが別のディレクトリを指定しなかった場合、デフォルトは /tmp になります ($TMPDIR は非常に広く認識されており、POSIX によって標準化されています)。次に、プロセス ID を含むファイル名プレフィックスを作成します。これはセキュリティ対策ではありません。これは、スクリプトの複数のインスタンスが互いのデータを踏みにじるのを防ぐための単純な並行処理です。(セキュリティのために、非パブリック ディレクトリでは予測不可能なファイル名を使用してください。) 2 行目はrm
、exit
シェルがシグナル SIGHUP (1)、SIGINT (2)、 SIGQUIT (3)、SIGPIPE (13) または SIGTERM (15)。' rm
' コマンドは、テンプレートに一致するすべての中間ファイルを削除します。このexit
コマンドは、ステータスがゼロ以外であることを保証します。何らかのエラーを示しています。'trap
' of 0 は、何らかの理由でシェルが終了した場合にもコードが実行されることを意味します。これは、「実際の作業」とマークされたセクションの不注意をカバーします。最後のコードは、終了時にトラップを解除する前に、残っている一時ファイルをすべて削除し、最終的にゼロ (成功) ステータスで終了します。明らかに、別のステータスで終了したい場合は、rm
およびtrap
行を実行する前に変数に設定してから、 を使用してexit $exitval
ください。
私は通常、次を使用してスクリプトからパスとサフィックスを削除します。これにより、$arg0
エラーを報告するときに使用できます。
arg0=$(basename $0 .sh)
エラーを報告するためにシェル関数をよく使用します。
error()
{
echo "$arg0: $*" 1>&2
exit 1
}
エラー終了が 1 つまたは 2 つしかない場合は、関数を気にしません。それ以上ある場合は、コーディングが簡単になるのでそうします。また、コマンドの使用方法の要約を提供するために呼び出される多かれ少なかれ精巧な関数も作成します。これもusage
、コマンドが使用される場所が複数ある場合に限ります。
getopts
もう 1 つの非常に標準的なフラグメントは、組み込みシェルを使用したオプション解析ループです。
vflag=0
out=
file=
Dflag=
while getopts hvVf:o:D: flag
do
case "$flag" in
(h) help; exit 0;;
(V) echo "$arg0: version $Revision$ ($Date$)"; exit 0;;
(v) vflag=1;;
(f) file="$OPTARG";;
(o) out="$OPTARG";;
(D) Dflag="$Dflag $OPTARG";;
(*) usage;;
esac
done
shift $(expr $OPTIND - 1)
また:
shift $(($OPTIND - 1))
"$OPTARG" を囲む引用符は、引数のスペースを処理します。Dflag は累積的ですが、ここで使用されている表記法では引数内のスペースを追跡できません。この問題を回避する (非標準の) 方法もあります。
最初のシフト表記は、どのシェルでも機能します (または、' $(...)
' の代わりにバックティックを使用した場合も同様です。2 番目は最新のシェルで機能します。括弧の代わりに角括弧を使用する代替手段もあるかもしれませんが、これは機能するので、それが何であるかを理解することは気にしません。
今のところ最後の秘訣は、GNU 版と非 GNU 版の両方のプログラムを持っていることが多いので、どちらを使用するかを選択できるようにしたいということです。したがって、私のスクリプトの多くは、次のような変数を使用します。
: ${PERL:=perl}
: ${SED:=sed}
そして、Perl または を呼び出す必要がある場合sed
、スクリプトは$PERL
orを使用し$SED
ます。これは、動作バージョンを選択できる場合や、スクリプトの開発中に動作が異なる場合に役立ちます (スクリプトを変更せずにコマンドにデバッグ専用オプションを追加できます)。(および関連する表記法については、シェル パラメーターの展開を参照してください。)${VAR:=value}