正しい引用符を使用してスクリプトファイルにコマンドを書き込む

正しい引用符を使用してスクリプトファイルにコマンドを書き込む

状況に何度かぶつかりました。いくつかの設定を行った後、次のように多少ランダムなコマンドを呼び出すシェル関数があります。

setup_and_run() {
    some_setup_commands
    "$@"
}

同じ設定が完了したら、コマンドを再実行するために使用できるスクリプトファイルを作成するようにこの関数を変更したいと思います。次のことができます。これは、コマンドにスペースやその他の奇妙な内容を含む引数がない場合に機能します。

setup_and_run() {
    some_setup_commands
    cat >>rerun.sh <<EOF
some_setup_commands
$@
EOF
    "$@"
}

一部のパラメータにスペースやその他の特殊文字が含まれていても、通常はスクリプトを正しい引用符で書くように変更する標準的な方法はありますか?

たとえば、 の 2 番目の定義に対して以下をsetup_and_run実行すると、

$ setup_and_run ls "my cat's \"password\""
my cat's "password"

これにより、以下rerun.shが含まれます。

some_setup_commands
ls my cat's "password"

そして私はそれに次のようなものを含めたいです

some_setup_commands
ls "my cat's \"password\""

または

some_setup_commands
ls my\ cat\'s\ \"password\"

またはそのようなもの。

状況が急になると、Pythonなどのサービスにアウトソーシングすることもできます。shlex.quote()しかし、可能であれば、シェル自体で行うことができるか、少なくとも軽量の標準のUNIXツールを使用して実行できることを探しています。

ベストアンサー1

%qBash、ksh93、zshではエスケープ文字を使用できますprintf組み込み

#!/bin/bash
# also works in ksh93, zsh
setup_and_run() {
    some_setup_commands
    cat >>rerun.sh <<EOF
some_setup_commands
$(printf '%q ' "$@")
EOF
    "$@"
}

printf -vBashでは、標準出力の代わりに変数に出力を書き込むことができます。

#!/bin/bash
printf -v cmd '%q ' "$@"
setup_and_run() {
    some_setup_commands
    cat >>rerun.sh <<EOF
some_setup_commands
$cmd
EOF
    "$@"
}

zshには異なるアプローチがあります。q パラメータ拡張フラグ、その後にj: :配列要素を連結するスペースが続きます。

#!/bin/zsh
setup_and_run() {
    some_setup_commands
    cat >>rerun.sh <<EOF
some_setup_commands
${(j: :)${(q)@}}
EOF
    "$@"
}

通常のshでは、これは難しいです。単語を引用する信頼できる方法は、単語を一重引用符で囲み、その中の各一重引用符を置き換えることです'\''。正しい置換を得ることは容易ではなく、sedまたはループへの外部呼び出しが必要です。

#!/bin/sh
quote_simple_command () {
  quoted=
  for raw in "$@"; do
    quoted="$quoted'"
    while case "$raw" in *\'*) true;; *) false;; esac; do
      quoted="$quoted${raw%%\'*}'\\''"
      raw="${raw#*\'}"
    done
    quoted="$quoted$raw' "
  done
  printf %s "${quoted% }"
}
setup_and_run() {
    some_setup_commands
    cat >>rerun.sh <<EOF
some_setup_commands
$(quote_simple_command "$@")
EOF
    "$@"
}

おすすめ記事