web-dev-qa-db-ja.com

エスケープされた文字列をエコーする方法

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でトリックを試しましたが、運はありませんでした。任意のガイダンスをいただければ幸いです。

7
Daniel

スクリプト内:

echo "${1//$/\\$}"

これは、シェルに組み込まれた単純な文字列置換です。構文は、グローバルリテラル文字列置換の${[VARIABLE_NAME]//[PATTERN]/[REPLACEMENT]}です。

なぜこれをしたいのかわからない。スクリプトは実際には、送信した文字列をレポートするだけです。 \$sad\$testは、単一の引用符を使用して'$sad$test'をエスケープすることを除いて、$とまったく同じ文字列に評価されます。

0
amphetamachine

文字列を「Shellquoted」で表示したいとします。

printf組み込みコマンドkshbash、および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. 一重引用符をシーケンス'\''に変更します。
  2. 結果を一重引用符で囲みます。

一重引用符は完全にリテラルであるため、これはボーンのようなシェルで機能します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は一重引用符での引用を「好き」であるように見え、場合によっては$''形式を使用します。他のシェルは主にバックスラッシュエスケープを使用しているようです(ただし、一部の文字が存在する場合は$''も使用します)。

7
Chris Johnsen

\$sad\$testを一重引用符で囲みます。

$ echo $Shell
/bin/bash
$ ./test.sh '\$sad\$test'
\$sad\$test
1
Mark McKinstry

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"を実行する必要があります。これにより、echowords$ 1で。 Wordの詳細については、ShellmanのIFS変数を参照してください。

echoは印刷前にいくつかの文字を変更するため、printf "%s\n" "$1"を実行する方がecho "$1"より安全であるとも言われていますが、まだ完全には理解していません。

編集:

echo文字を変更することについて、私はそれを理解しました。試してください:

echo "-n"
printf "%s\n" "-n"
./test.sh "-n"
cat1stpar "-n"
1
jfg956