エコーが引用文字を無視するのはなぜですか?


24

echoコマンドは、私はそれを与えることを完全なテキストを含めていません。たとえば、次の場合:

   $ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

以下を出力します:

echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `

'echoコマンドに含まれていた単一引用符()は含まれていません。二重引用符に切り替えた場合:

$ echo " echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `  "

echo何も出力しません!最初の例で単一引用符を省略し、2番目の例では何も出力しないのはなぜですか?入力したとおりに出力するにはどうすればよいですか?


3
ここで何を達成しようとしているのかよくわかりません。なぜ1つのエコーステートメントを別のエコーステートメントにネストするのですか?または、文字どおりにコマンドを出力しようとしていますか(例など)。
アンディスミス

エコー出力を別のファイルに挿入する必要があります

何をしようとしているのかを説明する必要があります。「次のテキストを追加したい: '…'ファイルに」など。
MikeyB

2
私はこれを5分間編集しようとしていましたが、目的の出力が何であるかを真剣に知りません。編集:わかりました、今は推測があるので、編集してみました。意図しない場合はお知らせください
Michael Mrozek

1
@Michael-すごい-あなたの編集の担当者がいれば、私はこれを本当に素晴らしい質問にしてくれます。
ランドール

回答:


33

あなたのシェルは両方、引用符を解釈している'"、彼らがさえに到達する前に、echo。私は通常、不要な場合でもエコーする引数を二重引用符で囲みます。例えば:

$ echo "Hello world"
Hello world

したがって、最初の例で、出力にリテラル引用符を含める場合は、エスケープする必要があります。

$ echo \'Hello world\'
'Hello world'

または、引用符付きの引数内で既に使用する必要があります(ただし、同じ種類の引用符は使用できません。または、とにかくエスケープする必要があります)。

$ echo "'Hello world'"
'Hello world'

$ echo '"Hello world"'
"Hello world"

2番目の例では、文字列の中央でコマンド置換を実行しています。

grep  $ARG  /var/tmp/setfile  | awk {print $2}

で始まるもの$もシェルによって特別に処理されます-シェルはそれらを変数として扱い、値で置き換えます。ほとんどの場合、これらの変数はどちらもシェルに設定されていないため、実際に実行されるだけです

grep /var/tmp/setfile | awk {print}

grep引数は1つしか表示されないため、引数は検索対象のパターンであり、データを読み取る場所はstdinであると想定しているため、入力待ちをブロックしています。2番目のコマンドがハングしたように見えるのはそのためです。

これは、引数を単一引用符で囲むと発生しません(これが最初の例がほぼ機能した理由です)。これは、必要な出力を取得する1つの方法です。

echo \'' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '\'

二重引用符で囲むこともできますが、$s をエスケープしてシェルが変数として解決しないようにする必要があります。

echo "' echo PARAM=\`  grep  \$ARG  /var/tmp/setfile  | awk '{print \$2}' \`    '"

7

Michael Mrozekの答えがこれをうまくカバーしているので、私はあなたの試みが彼らのやり方で振る舞う理由についてあまり詳しくは述べません。一言で言えば、一重引用符('…')の間のすべては文字どおりに解釈され(特に最初の'文字列はリテラル文字列の終わりを示します)、`との$間の特別な意味は保持され"…"ます。

単一引用符内に引用符がないため、単一引用符で囲まれた文字列内に単一引用符を入れることはできません。ただし、次のようなイディオムがあります。

echo 'foo'\''bar'

これはfoo'bar、の引数がecho単一引用符で囲まれた3文字の文字列fooで構成され、単一文字と連結'され('前の\文字列を通じて特殊な意味から文字を保護することで取得)、単一引用符で囲まれた3文字の文字列で連結されているため、出力されますbar。したがって、それは舞台裏で起こることではありませんが'\''、一重引用符で囲まれた文字列の中に一重引用符を含める方法と考えることができます。

複雑な複数行の文字列を印刷する場合は、こちらのドキュメントがより良いツールです。ヒアドキュメントは、2つの文字と、<<その後に続くマーカーなど<<EOFのテキスト、いくつかのテキスト行、その行のエンドマーカーのみで構成されます。マーカーが何らかの方法('EOF'または"EOF"or \EOFまたは 'E "" OF'または…)で引用されている場合、テキストは文字通りに解釈されます('通常の文字であることを除いて、単一引用符内のように)。マーカーがまったく引用されていない場合、テキストは二重引用符で囲まれた文字列のように解釈され$\`、特別なステータスが保持されます(ただし"、改行は文字どおりに解釈されます)。

cat <<'EOF'
echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `
EOF

1

さて、これは動作します:-

echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

ここでテスト:-

[asmith@yagi ~]$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '
 echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' ` 

0

ヒアドキュメントを使用するというGillesの提案は本当に素晴らしく、Perlのようなスクリプト言語内でも機能します。OPの問題に対する彼の回答に基づく具体的な例として、

use strict;
my $cmd = q#cat <<'EOF' # . "\n" .
          q#echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `# . "\n" .
          q#EOF# ;
open (my $OUTPUT, "$cmd |");
print $_ while <$OUTPUT>;
close $OUTPUT;

確かに、この例は少し工夫されていますが、たとえばSQLステートメントをpsql(ではなくcat)に送信するための便利な手法であることがわかりました。

(英数字以外のスペース以外の文字は、#上記の一般的な引用構造の代わりに使用できますが、この例ではハッシュが際立っているようで、コメントとして扱われません。)


-1

あなたの命令を取りましょう

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

そして、区切り文字として引用符で囲まれていない空白を使用して、最初に単語に分割します:

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“$2}' `    '”

次に、パラメーターの展開を行います:expandを実行します$…が、inside は実行しません'…'$2は空なので(例えばを介して設定しない限りset -- …)、空の文字列に置き換えられます。

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“}' `    '”

次に、引用の削除を行います。

Arg[1]=“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print”
Arg[2]=“} `    ”

これらの引数はechoに渡され、1つのスペースで区切られて次々に出力されます。

“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `    ”

この段階的な手順により、観察された動作がより明確になることを願っています。この問題を回避するには、次のように単一引用符をエスケープします。

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

これにより、単一引用符で囲まれた文字列が終了し、(エスケープされた)単一引用符が単語に追加され、単語を分割せずに新しい単一引用符で囲まれた文字列が開始されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.