Bourne Shellで$を含むエスケープ文字列をエコーするにはどうすればよいですか?
user@server:~$ cat test.sh
#!/bin/sh
echo $1
user@server:~$ ./test.sh \$sad\$test
$sad$test
私はそれがこのようにエスケープされたバージョンを返すことを望みます:
user@server:~$ ./test.sh \$sad\$test
\$sad\$test
私はsedとawkでトリックを試しましたが、運はありませんでした。任意のガイダンスをいただければ幸いです。
スクリプト内:
echo "${1//$/\\$}"
これは、シェルに組み込まれた単純な文字列置換です。構文は、グローバルリテラル文字列置換の${[VARIABLE_NAME]//[PATTERN]/[REPLACEMENT]}
です。
なぜこれをしたいのかわからない。スクリプトは実際には、送信した文字列をレポートするだけです。 \$sad\$test
は、単一の引用符を使用して'$sad$test'
をエスケープすることを除いて、$
とまったく同じ文字列に評価されます。
文字列を「Shellquoted」で表示したいとします。
printf組み込みコマンドksh、bash、およびzshはすべて%q
形式を理解します指定子。実際の出力は標準化されておらず、入力文字列によって異なります(以下を参照)。出力の形式にはばらつきがあるため、シェルからの出力を別のシェルで使用できない場合があります(特に、ターゲットシェルがash、- ダッシュなど)。
zshは、パラメータ展開で直接引用符を生成することもサポートしています。
echo "${(q)1}" # backslashes, with $'' for unprintable/invalid characters
echo "${(qq)1}" # single quotes
echo "${(qqq)1}" # double quotes
echo "${(qqqq)1}" # $'' quoting
「自分でロール」したい場合、最も簡単で信頼できる方法は、独自の単一引用符を生成することです。簡単なアルゴリズムは次のとおりです。
'\''
に変更します。一重引用符は完全にリテラルであるため、これはボーンのようなシェルで機能します1。単一引用符を表すには、現在の単一引用符で囲まれたスパンを終了し、円記号でエスケープされた単一引用符を指定して、文字列の残りの部分の単一引用符を再開します。上記のアルゴリズムは、連続した単一引用符と文字列の最初と最後の単一引用符の実行を処理する場合は非効率的ですが、実装は非常に信頼性が高く、非常に簡単です。
1 他の言語での一重引用符は、完全にリテラルではないことがよくあります(少なくとも、いくつかの限定された円記号のエスケープが許可されることがよくあります)。これが、生成する引用形式のすべての引用規則を知っていることが非常に重要である理由です(つまり、引用されたテキストが使用される言語/コンテキスト)。
ローカルにインストールしたシェルのprintf %q
の例を次に示します。
for s in /bin/ksh {,/opt/local}/bin/bash {,/opt/local}/bin/zsh
do
"$s" --version | head -1
"$s" -c 'printf "%q\n" "$@"' - \$sad\$test \'mixed\ quotes\" back\`tick
echo
done
出力:
version sh (AT&T Research) 1993-12-28 s+
'$sad$test'
$'\'mixed quotes"'
'back`tick'
GNU bash, version 3.2.48(1)-release (x86_64-Apple-darwin10.0)
\$sad\$test
\'mixed\ quotes\"
back\`tick
GNU bash, version 4.2.10(2)-release (i386-Apple-darwin10.7.0)
\$sad\$test
\'mixed\ quotes\"
back\`tick
zsh 4.3.9 (i386-Apple-darwin10.0)
\$sad\$test
\'mixed\ quotes\"
back\`tick
zsh 4.3.12 (i386-Apple-darwin10.7.0)
\$sad\$test
\'mixed\ quotes\"
back\`tick
kshは一重引用符での引用を「好き」であるように見え、場合によっては$''
形式を使用します。他のシェルは主にバックスラッシュエスケープを使用しているようです(ただし、一部の文字が存在する場合は$''
も使用します)。
\$sad\$test
を一重引用符で囲みます。
$ echo $Shell
/bin/bash
$ ./test.sh '\$sad\$test'
\$sad\$test
test.sh \$sad\$test
を実行すると、test.sh
スクリプトは、$sad$test
ではなく\$sad\$test
をパラメーターとして受け取ります。その理由は、\$
を呼び出す前に$
をtest.sh
に置き換えるシェルです。スクリプトが\$sad\$test
を受信するようにする場合は、test.sh '\$sad\$test'
またはtest.sh "\\\$sad\\\$test"
のいずれかを実行する必要があります。
これを試して、自分を納得させてください。
function cat1stpar() {
cat << EOF
$1
EOF
}
cat1stpar \$sad\$test
cat1stpar '\$sad\$test'
cat1stpar "\\\$sad\\\$test"
また、一般的に、echo $1
の使用は危険です。もしあなたがそうするなら:
./test.sh "aaa bbb"
./test.sh "aaa bbb"
出力は次のようになります。
aaa bbb
aaa bbb
ではなく:
aaa bbb
aaa bbb
2番目の出力が必要な場合は、少なくともecho "$1"
を実行する必要があります。これにより、echo
がwords$ 1で。 Wordの詳細については、ShellmanのIFS変数を参照してください。
echo
は印刷前にいくつかの文字を変更するため、printf "%s\n" "$1"
を実行する方がecho "$1"
より安全であるとも言われていますが、まだ完全には理解していません。
編集:
echo
文字を変更することについて、私はそれを理解しました。試してください:
echo "-n"
printf "%s\n" "-n"
./test.sh "-n"
cat1stpar "-n"