言う

言う

この質問は複雑に見えるかもしれませんが、そうではありません!考慮する:

% f() { echo "$@"; }
% f a
a
% f cmd -o"value with space"
cmd -ovalue with space
% f cmd -ovalue with space
cmd -ovalue with space
% f cmd -o'value with "quotes"'
cmd -ovalue with "quotes"
% f cmd -ovalue with "quotes"
cmd -ovalue with quotes

明らかに、「間隔値」属性は失われるパラメータにすぎず、同様に再入力すると二重引用符が「食べられます」。

必要な出力は、同じ出力を生成するために入力として再利用できる出力です。

BASHにはこれを可能にする機能が組み込まれていないようです。そうですか?

言う

何をしたいのかわからない場合:コマンドをシェル配列に保存し、ユーザーが出力をコピーしてシェルプロンプト(またはスクリプト)に貼り付けることができるように、これらの配列を標準出力として印刷したいと思います。配列内で同じコマンドを再現するため。

次の(愚かな)例を考えてみましょう。

> X=(echo "Bob's car is named \"Bobby\"")

正常なものがecho "${X[@]}"出力されます

echo Bobの車名は「Bobby」です。

しかし、一つ可能な正しい出力は次のとおりです。

echo Bobの車名は「Bobby」です。

答え1

その期間中の変身オプションの一つBashのパラメータ拡張はい(Bash 4.4以降は利用可能に見え、以前のバージョンでは「無効な置換」出力):

${parameter@operator}
拡張は価値の変化です。範囲または関連情報範囲それ自体は演算子の値に依存します。各オペレーター手紙です:

[...]
Q
拡張子は、値が次のような文字列です。範囲入力として再利用できる形式で引用してください。

bash-5.2$ f() { echo "${@@Q}"; }
bash-5.2$ f cmd -o'value with "quotes"'
'cmd' '-ovalue with "quotes"'
bash-5.2$ f cmd -ovalue with "quotes"
'cmd' '-ovalue' 'with' 'quotes'

答え2

あなたが探している機能は次のとおりです直列化直列化アメリカ英語)。

ここで、単純なコマンドは 1 つ以上の文字列配列なので、最終的には配列シリアライゼーションに要約されます。

execve()コマンドが外部コマンドの場合、NULバイトはCスタイル文字列としてシステムコールに渡されるため、文字列にNULバイトを含めることができないという追加の制限があります。ほとんどのシェルでは、execve()システムコールを含まないコマンド(組み込みコマンドや関数など)にも同じ制限が適用されます。唯一の例外はzshシェルです。

したがって、コマンド引数にNULバイトが含まれていないと仮定できる場合、直列化は簡単です。 NULで区切って印刷するだけです。

print0() {
  [ "$#" -eq 0 ] ||
    printf '%s\0' "$@"
}
print0 cmd -o"value with space" > file

4.4以降では、bashこれをパラメータリストとして読み直すことは次のとおりです。

readarray -td '' args < file

どこ-d ''NULバイトを区切り文字に設定-t変数に NUL を格納できない現在のバージョンの bash では、必ずしも不要な値から区切り文字を削除します。

次に、次のようにします。

"${args[@]}"

コマンドを実行します。

それともGNUを使用してくださいxargs

xargs -r0a file env

ただし、zsh以外のすべてのシェルと同様に、このシリアライゼーションの結果を変数に格納することはできず、シェル変数の値はNULバイトを持つことはできません。

JSON、XML、YAMLは複雑なデータ構造を直列化するための一般的な形式ですが、独自の問題もあります(たとえば、JSON文字列は文字で構成する必要がありますが、パラメータ文字列はランダムバイトの配列です)。さらに重要なのは、シェルがほとんどないということです。解析のサポートが組み込まれています(ksh93v-betaバージョンにはJSON解析のいくつかの実験的サポートがありましたが、バグがあり、最新バージョンでは削除されました)。

一部の言語にはシリアル化形式が組み込まれています。たとえばphpserialize()そしてそれに対応するunserialize()機能的ですが、phpコマンドの実行に適したAPIはありません。

解釈された言語の一般的なアプローチは、コードにシリアル化することです。たとえば、これがData::Dumper行うことです。パラメータとしてパラメータを持つ配列がある場合は、perlそれを保存してからPerlコードを評価して配列を再インポートできます。cmd-ovalue with space@array = ("cmd", "-o value with space")

bashやzshなどのKornに似たシェルでは、これが正確に行われているため、簡単に実行できますtypeset -p。 zshでは関数typeset -p argvでこれを行うことができますが、変数ではないのでそうではserialiseありません。では、位置パラメータは変数にマップされませんが、一時的な配列を使用することもできます。typeset -p @@bashargv

serialise() {
  local args
  args=( "$@" )
  typeset -p args
}
serialised=$(serialise cmd -o"value with space")

その後、逆シリアル化は次のようになります。

eval "$serialised"

これにより配列が作成されます$args(関数内で実行されると、配列はその関数に対してローカルになります)。

それから:

"${args[@]}"

コマンドを再実行してください。

シリアライゼーションは、シリアライゼーションを実行するときと同じシェルバージョン、同じオペレーティングシステム、および同じロケールを使用して実行する必要があります。バラより「他のスクリプトの内容として使用するために変数をエスケープしてください」への答えです。文字列を直列化する方法の詳細。


完全性のために、ksh93は多次元配列、構造、オブジェクトを含む他のシェルよりも複雑なデータ構造を持っているため、直列化と逆シリアル化のサポートが組み込まれています。

  • 直列化:print -C var
  • 逆シリアル化:read -C var

たとえば、次のように変数をコピーできます。

print -C var | read -C var_copy

答え3

他の答えに追加するには、bash(とzsh)にはprintf次のものがあります。%q

%q は、printf がその引数をシェル入力として再利用できる形式で出力するようにします。

$ f() { printf '%q ' "$@"; echo; }
$ f cmd -o"value with space" -o'"'\' -o"'" -o"'\\" -o'\' $'\n'
cmd -ovalue\ with\ space -o\"\' -o\' -o\'\\ -o\\ $'\n' 

上記は、これらのシェルに組み込まれている機能と連携する方法です。これに加えて、printfcoreutilsのGNUもこれをサポートしますが、出力はエスケープされずに引用されます。

$ f() { /bin/printf '%q ' "$@"; echo; }
$ f cmd -o"value with space" -o'"'\' -o"'" -o"'\\" -o'\' $'\n'
cmd '-ovalue with space' '-o"'\''' "-o'" '-o'\''\' '-o\' ''$'\n' 
"printf" は、残りの引数とともに書式文字列を "繰り返し" することで動作します。したがって、これらの例では、末尾に末尾のスペースがあります。

関連情報