これを正しく行う方法
まず、常に変数を引用してください。あなたがそれを正しく引用すればあなたがやろうとしていることはうまくいきます:
$ pwd
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]
$ ls
pullingATerdon
一貫性を保つために、あなたが選択した奇妙なファイル名を保持しました(なぜそれを選択したのかはわかりません)。
次に、pullingATerdon
変数のパスを割り当ててから、ファイルを開こうとします。
$ bacon="$(realpath pullingATerdon)"
$ echo "$bacon"
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]/pullingATerdon
$ ls $bacon
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':
予想通り、それは失敗します。しかし、正しく引用すると、次のようになります。
$ ls -l "$bacon"
-rw-r--r-- 1 terdon terdon 0 Mar 14 23:15 '/home/terdon/foo/\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]/pullingATerdon'
期待どおりに動作します。そして、はい、パスを(適切な)エディターで開くこともできますemacs "$bacon"
。問題なく動作します。はい、そうvim
です。エディターの選択は、残念ながら関係ありません。
なぜ失敗したのか
ケースで実際に何が起こったかをすばやく追跡する方法は、を使用することですset -x
(これをでオフに戻すset +x
)。これにより、シェルは、実行する各コマンドを実行前に出力します。シェルのデバッグメッセージをオンにしますset -x
。
$ set -x
$ /bin/ls $bacon
+ ls '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':
これは、とのls
3つの個別の引数で実行されたことを示しています。これは、シェルが引用符で囲まれていない文字列に対して単語分割とグロブ拡張を実行するために発生します。この場合、シェルはパス内のスペースを認識し、スペースで区切られた各文字列を個別の引数として読み取るため、問題は単語の分割です。'/home/terdon/foo/\e[92mM@r|<'
'+'\''|'\''|_e|\|\|0rth'
'[`-_-"]/pullingATerdon'
mkdir
あなたは私たちからのエラーメッセージ表示しているので、例はわずかに異なっているが、その者の第二のコマンドの呼び出しを。一度試した後、もう一度実行して質問の出力を取得したと思います。初めて実行したときは、次のようになります。
$ mkdir $(realpath pullingATerdon)
++ realpath pullingATerdon
+ mkdir '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
mkdir: cannot create directory ‘[`-_-"]/pullingATerdon’: No such file or directory
繰り返しになりますが、単語分割のため、1つではなく3つのディレクトリを作成しようとします。まず、(正常に)ディレクトリを作成しました/home/terdon/foo/\e[92mM@r|<
。
$ ls -l /home/terdon/foo/
total 8
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|<'
drwxr-xr-x 3 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]'
次に、+'|'|_e|\|\|0rth
現在のディレクトリに呼び出されるディレクトリも正常に作成されました。
$ ls -l
total 4
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:37 '+'\''|'\''|_e|\|\|0rth'
-rw-r--r-- 1 terdon terdon 0 Mar 15 00:36 pullingATerdon
そして、ディレクトリを作成しようとしました[`-_-"]/pullingATerdon
。これはmkdir
、デフォルトではサブディレクトリを作成しないため失敗しました(で実行すると、サブディレクトリが作成されます-p
)。
$ mkdir baz/bar
mkdir: cannot create directory ‘baz/bar’: No such file or directory
あなたの引用符で囲まれていない文字列が含まれているので/
、mkdir
二つのディレクトリのパスは、トップのものを見つけることを試み、失敗したと考えました。
それが失敗した理由ですが、発生したことはより複雑です。あなたが使用する文字列は、実際にシェルグロブ、特にあるグロブ範囲名前が5つの文字の一つであり、現在のディレクトリ内のすべてのファイルと一致し、`
、-
、_
または"
。現在のディレクトリにそのようなファイルがないため、グロブは何にも一致せず、bashのデフォルトの動作と同様に、自身を返します。
$ echo "[\`-_-\"]/pullingATerdon" ## some escaping is needed here
+ echo '[`-_-"]/pullingATerdon' ## but it echoes the right thing
[`-_-"]/pullingATerdon ## and matches nothing, so returns itself.
明確にするために、何かに一致するグロブを指定した場合、次のようになります。
$ echo [p]* ## any filename starting with a p
pullingATerdon
$ echo "[p]*" ## the string "[p]*"
[p]*
引用符で囲ま[p*]
れていないものは、一致するファイル名のリスト(この場合は1つだけ)に展開され、それがに渡されecho
ます。あなたがすべてのものを引用するべきもう一つの理由。
最後に、表示される実際のエラーは、コマンドを2回実行したときのものであり、最初のステップでcreateを試行すると失敗します/home/terdon/foo/\e[92mM@r|<
。これは、前の呼び出しですでにそのディレクトリが作成されているためです。
より一般的には、任意のファイル名で作業している場合は、常にシェルグロブを使用してください。このようなもの:
for file in *; do command "$file"; done
これはどのファイル名でも機能します。何が含まれていても。上記の例では、次のようにすることができます。
emacs /home/terdon/*92mM*/pullingATerdon
ターゲットファイルを一意に識別するグロブはすべて機能します。そうすれば、特殊文字を気にする必要がなく、シェルにそれらを処理させるだけで済みます。
いくつかの参考資料:
改行、スペース、またはその両方を含むファイル名を見つけて安全に処理するにはどうすればよいですか?:優れたGrey CatのWikiに関するFAQの1つ。
bash / POSIXシェルで変数を引用するのを忘れることのセキュリティへの影響:この回答の冒頭で参照したのと同じ投稿。シェル変数を正しく引用しなかった場合に失敗する可能性があるすべての事柄についての非常に詳細な説明。
シェルスクリプトで空白やその他の特殊文字が詰まるのはなぜですか?:シェルでの任意のファイル名の処理について知りたいことすべて。
二重引用符はいつ必要ですか?:引用符と変数の詳細、特に引用符を付ける必要がないいくつかのケース
vim "$bacon"