rm $(ls)を使用してファイルを削除することの欠点はありますか?


13

rm $(ls)ファイルの削除(またはrm -r $(ls)ディレクトリの削除)を使用しても安全かどうか疑問に思っていましたか?すべてのWebサイトで、このコマンドは他のコマンドよりもはるかに簡単に思えますが、人々はこれを行うために他の方法を与えます。


3
基本的な答え、いいえ。lsは特殊文字を処理できません。これをより詳細に説明するために少し答えを書くことができます
セルギーKolodyazhnyy

5
touch 'foo -r .. bar'; rm $(ls); 親ディレクトリはどこに行きましたか?また、これよりもさらに複雑な代替手段はどのように見えますか? rm *入力と思考がはるかに簡単で安全です(ただし、完全に安全ではありません。デニスの答えを参照してください)。
ピーターコーデス

1
優れた回答に加えて、ls実装によって異なる可能性があるため、非標準であることに注意してください。必要に応じて、findやなどの代替案を検討してくださいstatls他のコマンドやスクリプトで使用するために、人間が消費するためだけに使用する必要があります。
水田ランダウ

回答:


7

これは何をするつもりですか?

  • ls 現在のディレクトリ内のファイルをリストします
  • $(ls)ls引数としての場所の出力を置換しますrm
  • 本質的にrm $(ls)は、現在のディレクトリ内のすべてのファイルを削除することを目的としています

この写真の何が問題になっていますか?

lsファイル名の特殊文字を適切に処理できません。Unixユーザーは一般に異なるアプローチを使用することを勧めました。また、ファイル名のカウントに関する関連する質問でそれを示しました。例えば:

$ touch file$'\n'name                                                                                                    
$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ 

また、デニスの回答で適切に言及されているように、先頭にダッシュが付いたファイル名はrm、置換後の引数として解釈され、ファイル名を削除する目的に反します。

動作するもの

現在のディレクトリ内のファイルを削除します。したがって、globを使用しますrm *

$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ rm *
$ ls
$ 

findコマンドを使用できます。このツールは、現在のディレクトリ以外にも頻繁に推奨されます。ディレクトリツリー全体を再帰的に走査し、次の方法でファイルを操作できます。-exec . . .{} \;

$ touch "file name"                                
$ find . -maxdepth 1 -mindepth 1                                                                                         
./file name
$ find . -maxdepth 1 -mindepth 1 -exec rm {} \;                                                                          
$ ls
$ 

我々は(ノートこの1つはファイルだけのためであることを、あなたが使用する必要がありますそれにも採用することができるようにPythonは、ファイル名に特殊文字の問題を持っていないos.rmdir()os.path.isdir()あなたがディレクトリを操作する場合):

python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

実際、上記のコマンドは~/.bashrc、簡潔にするために関数またはエイリアスに変換できます。例えば、

rm_stuff()
{
    # Clears all files in the current working directory
    python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

}

そのPerlバージョンは

perl -e 'use Cwd;my $d=cwd();opendir(DIR,$d); while ( my $f = readdir(DIR)){ unlink $f;}; closedir(DIR)'

1
この"$(ls)"例は、ディレクトリにファイルが1つしかない場合にのみ機能します。タブ補完のみを使用してファイル名を展開することもできます。これが唯一の補完であるためです。
ピーターコーデス

実際、@ PeterCordesは、奇妙な理由で、1つのファイルでのみ機能します。それはそれから使用lsすることに対するもう1つの議論です:)私はそれを編集します
セルギー・コロディアズニー

私はそれを奇妙な理由とは呼びません:$(ls)単語分割を無効にするために引用するか、単語分割をさせます(悲惨な結果をもたらします)。データをコードとして扱わずに複数の文字列のリストを渡す唯一のクリーンな方法は、配列変数または\0セパレーターを使用することですが、シェル自体はそれを行うことができません。まだIFS=$'\n'最も危険ではありませんが、一致することはできませんfind -print0 | xargs -0。またはgrep -l --null。または、のようなもので問題全体を回避しますfind -exec rm {} +。(+複数の引数をrmの各呼び出しに渡すことに注意してください; WAYはより効率的です)。
ピーターコーデス

@PeterCordesうん、そこで完全に同意しました。しかしIFS=$'\n'、この場合もファイル名に改行があるため失敗します。そのため、wordsplittingは1つではなく2つのファイル名として扱います。しかし、奇妙な理由は、デフォルトIFSであるspace、tab、newlineで、オリジナルrm "$(ls)"も失敗するはずであり、ファイル名を2つの別々のファイルとして言ったように扱うべきですが、そうではなかったという事実です。通常、私が使用findして-exec、またはfind . . .-print0 | while IFS= read -d'' FILENAME ; do . . . doneファイル名に対処する構造。またはpython、を使用することもできます。その例を追加しました。
セルギーKolodyazhnyy 16

"$(ls)"引用符$(ls)は単語の分割からの展開を保護するため、常に1つの引数に展開されます。彼らが守るように"$foo"
ピーターコーデス

26

いいえ、安全ではありません。また、一般的に使用される代替手段はそれほど安全ではありませんrm *

には多くの問題がありますrm $(ls)。他の人がすでに回答でカバーしているように、の出力は内部フィールド区切りls文字にある文字で分割されます。

最良のシナリオ、それは単に機能しません。最悪のシナリオでは、ファイルだけを削除することを意図しましたが(ディレクトリは削除しません)、または一部のファイルを選択的に削除しますが、現在のディレクトリに-i名前のファイルがありますc -rf。しばらく様子を見てみましょう。

$ mkdir a
$ touch b
$ touch 'c -rf'
$ rm -i $(ls)
$ ls
c -rf

このコマンドrm -i $(ls)はファイルのみを削除し、各ファイルを削除する前に確認するように想定されていましたが、最終的に実行されたコマンドは読み取り

rm -i a b c -rf

それで、それはまったく別のことをしました。

rm *わずかに優れていることに注意してください。以前のディレクトリ構造では、ここで意図したとおりに動作しますが、というファイルがある場合は、-rfまだ運がありません。

$ mkdir a
$ touch b
$ touch ./-rf
$ rm -i *
$ ls
-rf

いくつかのより良い代替手段があります。最も簡単な方法は、rmとグロビングのみです。

  • コマンド

    rm -- *
    

    意図したとおりに機能し、--その後のすべてをオプションとして解釈してはならないことを示します。

    これは20年以上にわたってPOSIXユーティリティの構文ガイドラインの一部でした。それは広まっていますが、至る所に存在することを期待すべきではありません。

  • コマンド

    rm ./*
    

    グロブの展開方法が異なるため、呼び出されたユーティリティのサポートは必要ありません。

    上記の私の例では、echoを前に付けることで最終的に実行されるコマンドを見ることができます。

    $ echo rm ./*
    rm ./a ./b ./-rf
    

    リーディング./により、rmがオプションのようなファイル名を誤って処理することを防ぎます。


1
非常に良い点は、拡張子を持つ-の後のファイル名がにフラグになることrmです。+1
セルギーコロディアズニー

1
さらに厄介な:touch 'foo -rf .. barlsの出力にパス区切り文字を生成できない限り、攻撃者が親ディレクトリよりも上位に到達できるとは思わない。
ピーターコーデス

@Peterの攻撃者は、最初に特許ディレクトリを削除するための書き込み権限を持っている必要があります。
セルギーKolodyazhnyy 16

1
@PeterCordes rmのすべてのバージョンがこのフェイルセーフを備えているかどうかはわかりませんが、Ubuntu、openSUSE、およびFedoraでは、rm: refusing to remove '.' or '..' directory: skipping '..'親ディレクトリを削除しようとすると、または同様のことを言います。
デニス

@Serg:攻撃者はそのファイル名を含む.zipを送信するだけで、それを抽出して内容を削除しようとすることで自分自身を撃つことができます。または、そのファイル名を/ var / tmpなどに作成します。
ピーターコーデス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.