既存の回答の問題:
- スペースまたは改行が埋め込まれたファイル名を処理できない。
rm
引用符で囲まれていないコマンド置換 ( ) で直接呼び出すソリューションの場合、rm `...`
意図しないグロビングのリスクが追加されます。
- ファイルとディレクトリを区別できない (つまり、ディレクトリがたまたま最近変更された 5 つのファイルシステム項目の中にあった場合、実質的に保持されるファイルは 5 つ未満になり、ディレクトリへの適用
rm
は失敗します)。
wnoise の回答はこれらの問題に対処していますが、解決策はGNU固有です (そして非常に複雑です)。
これは実用的なPOSIX 準拠のソリューションですが、注意点が 1 つだけあります:改行が埋め込まれたファイル名を処理することはできませんが、ほとんどの人にとって現実世界の懸念事項ではないと思います。
ls
記録として、出力を解析することが一般的に良い考えではない理由の説明を以下に示します: http://mywiki.wooledge.org/ParsingLs
ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}
注: このコマンドは現在のディレクトリで動作します。ディレクトリを明示的(...)
cd
に対象にするには、subshell( ) を次
(cd /path/to && ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {})
のように使用します。以下のコマンドにも同様に適用されます。
上記は、ファイル名ごとに個別に呼び出す必要があるため、非効率的です。
ただし、プラットフォーム固有の実装により、この問題を解決できる場合があります。xargs
rm
xargs
GNUで動作 xargs
する解決策は、 を使用すること-d '\n'
です。これにより、xargs
各入力行が個別の引数と見なされますが、一度にコマンド ラインに収まる数の引数が渡されます。
ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --
注: Option -r
( --no-run-if-empty
) は、inputrm
がない場合に が呼び出されないようにします。
GNUとBSD ( macOSを含む)の両方で動作 xargs
xargs
する解決策は、技術的にはまだPOSIXに準拠していませんが、最初に改行を( ) 文字に変換した後、 -separated 入力を処理するために使用することです。これにより、(通常は) すべてのファイル名も渡されます。一度に:-0
NUL
NUL
0x0
ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --
説明:
ls -tp
最近変更されたファイルシステム項目の名前を降順 (最近変更された項目が最初) に並べ替えて ( -t
)、そのことを示す末尾にディレクトリを付けて出力/
します ( -p
)。
- 注: 現在のディレクトリ以外のディレクトリをターゲットにするために上記のサブシェル アプローチが必要になるのは、フル パスではなく、ファイル/ディレクトリ名
ls -tp
のみを常に出力するという事実です( )。(cd /path/to && ls -tp ...)
grep -v '/$'
-v
次に、末尾に/
( ) がある行( ) を省略して、結果のリストからディレクトリを除外し/$
ます。
- 警告:ディレクトリを指すシンボリック リンクは、技術的にはそれ自体がディレクトリではないため、そのようなシンボリック リンクは除外されません。
tail -n +6
リストの最初の5 つのエントリをスキップし、実際には、最近変更された 5 つのファイルを除くすべてのファイルを返します。ファイル
を除外するには、を に渡す必要があることに注意してください。N
N+1
tail -n +
xargs -I {} rm -- {}
rm
(およびそのバリエーション) は、これらすべてのファイルに対して onを呼び出します。一致するものがまったくない場合は、xargs
何もしません。
xargs -I {} rm -- {}
は、各入力行を全体として{}
表すプレースホルダーを定義するため、入力行ごとに 1 回呼び出されますが、スペースが埋め込まれたファイル名は正しく処理されます。rm
--
すべての場合において、で始まるファイル名-
が.rm
一致するファイルを個別に処理するか、シェル配列に収集する必要がある場合の、元の問題のバリエーション:
# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done
# One by one, but using a Bash process substitution (<(...),
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)
# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements