実行する前に完全なシェルスクリプトを読む方法は?

実行する前に完全なシェルスクリプトを読む方法は?

通常、スクリプトを編集すると、そのスクリプトを実行しているすべての目的でエラーが発生しやすくなります。

例:

sleep 20

echo test

このスクリプトを実行すると、bashは最初の行(たとえば10バイト)を読み取り、スリープモードに入ります。回復すると、バイト 10 から始まるスクリプトの内容が異なる場合があります。まったく異なる分岐の異なる行の途中で実行が開始されることがありますif。実行中のスクリプトが削除されます。

それでは、後で編集しても実行中のインスタンスに影響を与えないように実行する前に、シェルスクリプト全体をどのように読み取ることができますか?

答え1

はい、シェルはbashファイルを一度に1行ずつ読み込むことに特別な注意を払います。したがって、インタラクティブに使用するのと同じ方法で動作します。

ファイルを検索できない場合(パイプなど)、bash一度に 1 バイトずつ読み取ってその\n文字だけを読み取ることがわかります。ファイルを検索できる場合は、ブロック全体を一度に読み取って最適化しますが\n

これは、次のことができることを意味します。

bash << \EOF
read var
var's content
echo "$var"
EOF

または、自分で更新するスクリプトを作成してください。その保証を提供しないと、そうすることはできません。

今、私はこのようなことをしたくないかもしれませんが、この機能が役に立つよりも邪魔になることが多いことがわかります。

これを回避するには、ファイルを所定の場所に変更しないようにすることができます(たとえば、コピーを変更してからコピーを所定の場所に移動します(たとえば、sed -i一部のperl -pi編集者が実行する操作など)。

または、次のようにスクリプトを作成できます。

{
  sleep 20
  echo test
}; exit

(閉じ括弧の直前の中かっこ内に入れることもできますが、exit;と同じ行にあることが重要です。)}

または:

main() {
  sleep 20
  echo test
}
main "$@"; exit

タスクの実行を開始する前に、シェルはスクリプトを読み取る必要がありますexit。これにより、シェルはスクリプトから再度読み取られなくなります。

これはスクリプト全体がメモリに保存されることを意味します。

これはスクリプトの解析にも影響します。

たとえば、次のようになりますbash

export LC_ALL=fr_FR.UTF-8
echo $'St\ue9phane'

UTF-8でエンコードされたU + 00E9を出力します。ただし、次のように変更すると:

{
  export LC_ALL=fr_FR.UTF-8
  echo $'St\ue9phane'
}

\ue9コマンドの解析時に適用された文字セットに展開されます。この場合今後コマンドがexport実行されます。

sourceまた、akaコマンドを使用する場合、.一部のシェルではソースファイルと同じ問題が発生します。

bashただし、このコマンドはファイルを解釈する前にsourceファイルを完全に読み取るため、そうではありません。具体的に書かれている場合は、bashスクリプトの先頭に以下を追加することで実際にそれを利用できます。

if [[ ! $already_sourced ]]; then
  already_sourced=1
  source "$0"; exit
fi

(私はこれに依存しません。将来のバージョンではこの動作が変更される可能性があると想像できますが、これは現在の制限と見なすことができますbash(bashとAT&T kshは、私たちが知っている限りPOSIXのように動作する唯一のシェルです) . already_sourcedBASH_SOURCE 変数の内容に影響を与えるということは、言うまでもなく、変数が環境にないと仮定するため、少し脆弱です。

答え2

ファイルを削除するだけです(ファイルをコピー、削除、コピーの名前を元の名前に戻すなど)。実際、多くの編集者がこれを行うように構成できます。ファイルを編集して変更されたバッファをファイルに保存したら、ファイルを上書きせずに古いファイルの名前を変更して新しいファイルを作成し、新しい内容を新しいファイルに入れます。したがって、実行中のすべてのスクリプトは問題なく続行する必要があります。

RCS(vimやemacs用)などの単純なバージョン管理システムを使用すると、変更履歴を保持する2つの利点があります。チェックアウトシステムはデフォルトで現在のファイルを削除して正しいスキーマで再生成する必要があります。 (もちろん、そのようなファイルをハードリンクすることに注意してください)。

答え3

使用:

{
  ... your code ...

  exit
}

{}Bashは実行前にブロック全体を読み取り、このexitコマンドはコードブロックの外側に何も読み込まないようにします。

実行するのではなく、「インポート」スクリプトの場合は、次を使用してください。

{
  ... your code ...

  return 2>/dev/null || exit
}

答え4

スクリプトをブロックにラップするのが{}最善のオプションかもしれませんが、スクリプトを変更する必要があります。

F=$(mktemp) && cp test.sh $F && bash $F; rm $F;

2番目に良い選択になります(家庭一時ファイルシステム)欠点は、スクリプトで$ 0を使用すると$ 0が削除されることです。

このようなものを使用することは、F=test.sh; tail -n $(cat "$F" | wc -l) "$F" | bashファイル全体をメモリに保存し、$ 0を破壊する必要があるため理想的ではありません。

最後の変更時間、読み取りロック、およびハードリンクが妨げられないように、元のファイルへのアクセスは避けてください。これにより、ファイルの実行中にエディタを開いたままにすることができ、rsyncはバックアップ用のファイルを不必要にチェックサムせず、ハードリンク機能が期待どおりに機能します。

編集中にファイルを置き換えることは機能しますが、他のスクリプト/ユーザーに対して実行できないか、人々が忘れてしまう可能性があるため、それほど強力ではありません。ハードリンクが再び切断されます。

関連情報