web-dev-qa-db-ja.com

echo \\ *-bashのバックスラッシュエスケープ動作、逆方向に評価されますか?

だからbashでは、私がするとき

echo \*
*

*はエスケープされ、文字どおりに解釈されるため、これは正しいようです。

しかし、私はそれを理解できない

echo \\*
\*

最初のバックスラッシュは2番目のバックスラッシュをエスケープすると思ったので、2つのバックスラッシュ「\\」はリテラルで1つの「\」を与えます。そして*はその特別な意味を持って続いた。私は期待していました:

echo \\*
\file1 file2 file3

回答の概要:\は文字どおりに解釈されるため、echo \*echo a*と同様に動作し、文字 "a"で始まるすべてのファイルを検索します。

フォローアップの質問、正確に印刷したい場合

\file1 file2 file3

どのコマンドを使用すればよいですか?例えば次のように私はスペースが欲しい

echo \\ * 
\ file1 file2 file3
6
Weishi Zeng

現在のディレクトリにバックスラッシュで始まる名前のファイルがない場合、これは予想される動作です。バッシュ は、既存のファイル名と一致するように*を展開しますが、

パターンが既存のファイル名またはパス名と一致しない場合、パターン文字列は変更されません。

\で始まるファイル名がなかったため、パターンはそのままで、echoには引数\*が指定されています。この振る舞いはしばしば混乱を招き、zshのような他のシェルにはそれがありません。あなた shopt -o failglob を使用してBashで変更できます。これにより、zshと同様にエラーが発生し、診断に役立ちます誤動作する代わりに問題。

*および?パターン文字は、Wordのどこにでも出現でき、前後の文字は文字どおり一致します。そのため、echo \\*echo \\ *はこのような異なる出力を提供します。最初のコードは\で始まるすべてのコードに一致し(失敗)、2番目のコードは\を出力し、次にすべてのファイル名を出力します。 。


安全に必要な出力を取得する最も簡単な方法は、おそらく printf を使用することです。

printf '\\'; printf "%s " *

echo *は、異常なファイル名に-または\が含まれている場合は安全ではありません。

7
Michael Homer

これを試して:

_printf "\\%s" "$(echo *)"
_

説明:

  • printfは、フォーマット引数と、フォーマット文字列に代入される0個以上の引数を取ります
  • 形式の_\\_は、_echo \\_と同じ意味です。
  • _%s_は、次の引数を取り、それを結果に代入することを意味します
  • "$(echo *)" means:_echo *_を実行し、結果をprintfの単一の引数に入れます。 printfの動作方法のため、単一の引数に入れる必要があります
1
daniel kullmann

試してみてください...

set file*
printf %s\  "\\$@"

配列の先頭にバックスラッシュを追加します-最初の要素のみです。終わりでも同じことができます。あなたはそれらをすべて\\バックスラッシュのような:

printf \\%s file*

...または...

set file*; IFS=\\; printf %s\\n "$*"
1
mikeserv

いいえ、bashエスケープ文字 は、次の文字のリテラル値を左から右に保持するため、\\*はパターン\*を提供します。

このパターンは ファイル名拡張 で実行されます パターンマッチング ルール。

したがって、\*は、\で始まり、その後に続くすべてのファイルとして解釈されます。あなたの場合、それは何も一致せず、bashはパターン\*echoに変更せずに残しました。

1
cuonglm

技術的にのみ、またはEdgeの場合に限り、質問の後半の他のすべての回答(「どのコマンドを使用する必要がありますか?」)は間違っていると思います。私はこれが他の答えの問題に対処すると信じています:

(set -- *; echo "\\$*")

(FWIW)すべての書き込みが単一のコマンドで行われている
printf '\\'; printf "%s " *; echoとは異なります)。

  • サブシェルを使用して、外側のスコープの位置パラメータの設定/変更/コンテキストを回避します。
  • 単純に、set *は定位置パラメーター($1$2$3など...)を現在のディレクトリ内のファイルの名前に設定します。 (dotglobオプションが設定されているかどうかに基づいて、.(ドット)で始まるファイル名が含まれるか含まれない。)
  • 名前がset *(ダッシュ)で始まるファイルがある場合、-は失敗する可能性があります(*リストの最初のファイルです)。 set -- *がこれを処理します。 --は、「これ以降はすべて引数であり、オプションではない」と言います。
  • $*は位置パラメータ($1 $2 $3 …)のリストです。 パラメータをスペースで区切って\\を指定すると、\(問題のコマンド例)と同様に、echo \\ *が行の先頭に出力されますが、展開の妨げにはなりません後続の$*
  • $*は、位置パラメータ($1 $2 $3 …)のリストですパラメータは$IFSの最初の文字で区切られています$IFSの最初の文字がスペースではない可能性がある場合は、
(set -- *; IFS=" "; echo "\\$*")

もう少し詳細なオプションが含まれます

(set -- *; printf "%s\n" "\\$*")

そして

(set -- *; printf "\\%s\n" "$*")

これらは、-eオプションがなくても、echoが引数文字列のバックスラッシュを解釈する可能性がある環境でより安全です。

繰り返しますが、IFS=" ";の最初の文字がスペースではない可能性がある場合は、$IFSを追加してください。

1
Scott

1:グロブ

echo \*を実行すると、*が表示されます

*はエスケープされ、文字どおりに解釈されるため、これは正しいようです。

はい、エスケープ\(引用符)を使用すると、それ以外の特殊文字は通常の文字になるため、*としてそのまま出力されます。

しかし、それを理解できないので、echo \\*を実行すると\*が表示されます

(グロビングを介して)展開する「文字列」について考える必要があるため。文字列は\\*です。これは、引用符で囲まれておらず、特殊文字(バックスラッシュ)があるためです最初のバックスラッシュは2番目を引用符で囲みますとなり、oneになります。バックスラッシュとそれに続くアスタリスク(\*)。文字列:バックスラッシュ(\)の後に何か(*)が続くと、文字どおり一致する必要があります。つまり、バックスラッシュで始まり、さらに文字が続くファイル名です。 \a\one\fileなどのファイルがない場合、\*のグロビングは失敗します。つまり、次のようになりますnot「ファイルのリスト」。 bashの一部のオプション(failglobまたはnullglob)が設定されていない場合、文字列\*が残り、そのまま出力されます。あなたは\*を取り戻します。

2:連結

私は期待していました:

    $ echo \\*
    \file1 file2 file3

上記で、なぜ\\*が文字列\*になるのか、つまりone stringであることが明らかになりました。次のように2つの文字列を連結することはできません。

$ a='\'
$ b='file1 file2 file3'
$ echo "$a$b"
/file1 file2 file3

つまり、連結するにはtwo文字列が必要です。 1つの文字列はリテラル\の変数で、もう1つの文字列は現在のディレクトリにあるファイルのリスト文字列としてです。これを達成する1つの方法は次のとおりです。

$ set -- *               # get the list of files as arguments.
$ echo '\'"$*"           # or "\\$*".  Convert arguments to an string $*
                         # and concatenate '\' to it.
\file1 file2 file3
1
Isaac