web-dev-qa-db-ja.com

realpathまたはreadlinkによって返される絶対パスをサニタイズまたはエスケープするにはどうすればよいですか?

realpathおよびreadlinkは絶対パスを返します。

+akiva@X230:~$ realpath ZannaIsAwesome
/home/akiva/ZannaIsAwesome

そのようなパスは簡単に対処できます。ただし、次のような問題が発生する可能性があります。

enter image description here

例えば:

enter image description here

したがって、このような名前は、他のコマンドにフィードできるようにサニタイズする必要があります。ユースケースは次のようなものです。

+a@X230:~/\e[92mM@r|< $hu+'|'|_e|\|\|0rth [`-_-"]$ bacon=$(realpath pullingATerdon)
+a@X230:~$ vim $bacon 

言うまでもなく、vim $baconは期待どおりに動作しません。

他のコマンドで動作するように絶対パスをサニタイズするにはどうすればよいですか?

8
Akiva

これを正しく行う方法

まず、 常に変数を引用符で囲みます 。あなたがそれをきちんと引用すればあなたがしようとしていることはうまく働く:

$ 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"は問題なく動作します。 OK、vimなども同様です。残念ながら編集者の選択は関係ありません。


なぜ失敗したのか

ケースで実際に何が起こったのかを簡単に追跡するには、set -xset +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'/home/terdon/foo/\e[92mM@r|<''+'\''|'\''|_e|\|\|0rth''[`-_-"]/pullingATerdon'の3つの個別の引数で実行されたことを示しています。これは、シェルが引用符で囲まれていない文字列に対して Word分割とグロブ拡張 を実行するために発生します。この場合、問題はWord分割です。シェルはパス内のスペースを認識し、スペースで区切られた各文字列を個別の引数として読み取るためです。

mkdirの例は少し異なりますが、それはコマンドのsecond呼び出しからのエラーメッセージを表示しているためです。一度試してみてから、もう一度実行して質問の出力を取得したと思います。初めて実行したときは、次のようになりました。

$ 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

繰り返しますが、Wordの分割により、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は2つのディレクトリのパスと見なし、一番上のディレクトリを見つけようとして失敗しました。

それが失敗した理由ですが、起こったことはより複雑です。実際に使用した文字列は、シェルグロブ、特に グロブ範囲 です。これは、名前が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に渡されます。すべてのことを引用すべきもう1つの理由。

最後に、表示される実際のエラーは、コマンドを2回実行したときのものであり、/home/terdon/foo/\e[92mM@r|<を作成しようとしたときに最初のステップで失敗します。


より一般的には、任意のファイル名で作業していることに気づいたときは、常にシェルグロブを使用します。このようなもの:

for file in *; do command "$file"; done

これはどのファイル名でも機能します。何が含まれていても。上記の例では、次のことができます。

emacs /home/terdon/*92mM*/pullingATerdon

ターゲットファイルを一意に識別する任意のグロブが行います。そうすれば、特殊文字を心配する必要がなく、シェルにそれらを処理させるだけで済みます。


便利なリファレンス:

  1. 改行、スペース、またはその両方を含むファイル名を見つけて安全に処理するにはどうすればよいですか? :優れたGray Cat's WikiのFAQの1つ。

  2. bash/POSIXシェルで変数を引用するのを忘れた場合のセキュリティへの影響 :この回答の冒頭で参照した同じ投稿。シェル変数を正しく引用しなかった場合に、うまくいかない可能性があるすべてのことについての非常に詳細な説明。

  3. シェルスクリプトが空白文字やその他の特殊文字で詰まるのはなぜですか? :シェルでの任意のファイル名の処理について知りたいことすべて。

  4. 二重引用符が必要な場合 :引用符と変数の詳細、特に引用符を必要としないいくつかのケース

11
terdon