file_<your_stuff_here>.txt
bashスクリプトでlsの出力を評価またはバックティックすることは、危険なほど与えられたプレフィックスとサフィックスを持たなければならないという制約を持つのに十分に病理的な一連のファイル名またはファイル名を持っていますか? 「危険」とは、関連ディレクトリにファイルを作成する権限がある人なら誰でもすべてのコマンドを実行できることを意味します。
たとえば、bashスクリプトを次のようにしましょう。
#!/usr/bin/env bash
hello=(`ls -t`)
ファイルシステムは、ファイル名を引用符で囲み、スペースなどの特殊文字を削除するようです。これらすべてを捉えることができますか?
答え1
ファイル名...危険評価
確かに。
$ touch 'file $(date >&2).txt'
$ bash -c 'eval ls *'
Wed 03 Feb 2021 06:07:08 PM EET
ls: cannot access 'file': No such file or directory
ls: cannot access '.txt': No such file or directory
eval
物をランダムに入れないでください
それとも、bashスクリプトからbacktick lsの出力ですか?
これが何を意味するのかよく分からない。
おっしゃった例をお話ししたら、
hello=(`ls -t`)
または、より合理的な構文を使用してください。
files=( $(ls -t) )
ls
次に、wordsplitの出力を配列に取得します。
$ declare -p files
declare -a files=([0]="file" [1]="\$(date" [2]=">&2).txt")
命令注入が可能になる前でもここで最大の問題はファイル名にスペースが含まれていると問題が発生します。、我々は1つの代わりに2つの配列項目を取得します。関連ページを見るlsを解析するGregのwikiにあります。いいえ、引用符を追加してもこの問題を解決することはできません。噴射そうではありません。
だからls
そこでは使用しないでください。シェルにファイル名のリストを生成するよう依頼します。
files=(*)
declare -p files
declare -a files=([0]="file \$(date >&2).txt")
ここで唯一の問題は、Bashが日付別にファイルをソートする良い方法を提供しないことです。ls -t
はい誘惑的だ。良いオプションは、ファイル名自体に日付を入れることですしたがって、デフォルトの並べ替えを使用すると、日付で並べ替えることができます。または日付で並べ替えることができるので、Zshを使用してください。。または醜いハッカー問題を解決してください(警告、このソリューションはまだテストされていません)。
eval
次のように、シェルスクリプトのみが必要なエントリにファイル名を渡す必要がある場合でも、同じ問題が発生します。
ssh somehost "do something with $file" # wrong
ssh somehost "do something with '$file'" # still wrong, the name
# could contain single ticks
ファイルシステムは、ファイル名を引用符で囲み、スペースなどの特殊文字を削除するようです。これらすべてを捉えることができますか?
ああ、そう、いいえ、そうではありません。ファイルシステムが特殊文字がシェルに保存されないように措置を講じた場合、unix.SEの投稿の半分は必要ありません。
あまりにも多くの知識に苦しんでいる場合は、David Wheelerの記事を読んでください。Unix/Linux/POSIX ファイル名の変更: 制御文字 (改行文字など)、先行ダッシュ、その他の問題
そして彼のもう一つは、シェルのファイル名とパス名:正しく取得する方法。多くの関連トピックがunix.SEでも議論されています。
また、引用文も役に立ちません。
$ touch '"quoted name"' 'othername' # two files
$ printf "%s\n" $(ls) # oops
othername
"quoted
name"
$ printf "%s\n" * # this works better
othername
"quoted name"
単語分割が発生した場合、引用は単なる一般的な文字であるからです。 (設定にIFS
引用符が含まれていないと、状況が悪化する可能性があります。)また、名前が引用符で囲まれていても途中でまだ引用符がある可能性があります。、これを破るために。また、正しくエスケープまたは引用するように注意する必要があります。
引用のためのGNU ls。バージョンと設定によって異なる:
$ ls -l
total 0
-rw-rw-r-- 1 ilkkachu ilkkachu 0 Feb 3 18:30 othername
-rw-rw-r-- 1 ilkkachu ilkkachu 0 Feb 3 18:30 '"quoted name"'
これはと同じですls --quoting-style=shell
。ところで、すべての改行、ドル記号、引用符には正しいようです。しかし、それが正しく行われると信じていますか?これにより、そして正しく使用する方法がわかっている場合は、それを使用してソートされたリストを取得できます。
答え2
hello=(`ls -t`)
この形式は安全に見えます。 Bashは構文として解釈せず、コマンド置換の結果に対してのみ分割+globを実行します。
a=(`echo '[0]=1'`)
typeset -p a
declare -a a=([0]="[0]=1") # aha!
ただし、(あいまいな)declare "var=val"
またはlocal "var=val"
形式は次のように動作するため、そうではありませんeval "var=val"
。
cd "$(mktemp -d)"
touch '$(yes BOOBS >&2&)'
declare -a "a=($(ls))"
BOOBS
BOOBS
BOOBS
...
などと同じですdeclare -a a="($(ls))"
。declare -a a="(`ls`)"
それとも引用しない変数が配列として宣言されている場合、フォームにも同様に適用されます。
cd "$(mktemp -d)"
a=(1 2 3)
touch '($(yes BOOBS >&2&))'
declare a=$(ls) # I forgot that a was an array
BOOBS
BOOBS
BOOBS
...