回答:
既存の回答の問題:
rm
引用符で囲まれていないコマンド置換(rm `...`
)を直接呼び出すソリューションの場合、意図しないグロブのリスクがさらに追加されます。rm
できず、ディレクトリへの適用が失敗します)。wnoiseの答えはこれらの問題に対処しますが、解決策はGNU固有です(そして非常に複雑です)。
ここでは、警告が1つだけ付いた実用的なPOSIX準拠のソリューションを示します。改行が埋め込まれたファイル名は処理できませんが、実際の問題はほとんどの人には関係ないと思います。
参考までに、ls
出力を解析するのが一般によくない理由の説明は次のとおりです。http://mywiki.wooledge.org/ParsingLs
ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}
上記はファイル名ごとに 1回呼び出す必要があるため、非効率的です。
プラットフォームによっては、この問題を解決できる場合があります。xargs
rm
xargs
GNU xargs
を使用している場合は、を使用します-d '\n'
。これによりxargs
、各入力行を個別の引数と見なしますが、コマンドラインに一度に収まるだけの数の引数を渡します。
ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --
-r
(--no-run-if-empty
)は、rm
入力がない場合に呼び出されないようにします。
あなたが持っている場合はBSD xargs
(上を含むMacOSのを)は、使用することができます-0
処理するためNUL
に最初の翻訳改行の後に、区切られた入力をNUL
(0x0
)文字を、また、(通常は)通過するすべてのファイル名。一度(意志もGNUとの仕事xargs
):
ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --
説明:
ls -tp
ファイルシステムアイテムの名前を、それらが最後に変更された順に並べ替えて、降順で(最も最近変更されたアイテムを最初に)(-t
)/
表示します-p
。grep -v '/$'
次に-v
、末尾に/
(/$
)がある行()を省略して、結果のリストからディレクトリを削除します。
tail -n +6
リストの最初の5つのエントリをスキップし、実際には、最後に変更された5つのファイルを除いてすべてを返します。ファイルN
にN+1
は、に渡す必要があることに注意してくださいtail -n +
。xargs -I {} rm -- {}
(およびそのバリエーション)次にrm
、これらすべてのファイルを呼び出します。一致するものがまったくない場合、xargs
何もしません。
xargs -I {} rm -- {}
定義プレースホルダ{}
各入力ラインを表し、全体としては、そう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
ls
現在のディレクトリにいない場合、ファイルへのパスには「/」が含まれますが、これはgrep -v '/'
何にも一致しません。私grep -v '/$'
はあなたがディレクトリだけを除外したいものだと信じています。
grep
コマンドが概念的にも明確になります。ただし、ここで説明する問題は単一のディレクトリパスでは発生しないことに注意してください。たとえば、ls -p /private/var
まだファイル名だけを出力します。複数のファイル引数を(通常はグロブを介して)渡した場合にのみ、出力に実際のパスが表示されます。たとえば、ls -p /private/var/*
(も含めない限り、一致するサブディレクトリの内容も表示されます-d
)。
ディレクトリ内の最新のファイルのうち5つ(または任意の数)を除くすべてを削除します。
rm `ls -t | awk 'NR>5'`
ls -t
へls -td *.bz2
ls -t | awk 'NR>1'
(最新のものだけが必要でした)。ありがとう!
ls -t | awk 'NR>5' | xargs rm -f
パイプが必要で、削除するものが何もない場合にエラーを抑制する必要がある場合。
touch 'hello * world'
削除されます。
(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm
このバージョンでは、スペースを含む名前がサポートされています。
(ls -t|head -n 5;ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm
--no-run-if-empty
しxargs
て(ls -t|head -n 5;ls)|sort|uniq -u|xargs --no-run-if-empty rm
回答を更新してください。
touch 'foo " bar'
残りのコマンド全体が破棄されます。
xargs -d $'\n'
(以外使用する必要があり、入力ストリームNUL-区切りかかわらず、あなたのコンテンツに注入引用符によりls
に本当に右行うことは)理想的なオプションです。
thelsdjの答えのより簡単な変形:
ls -tr | head -n -5 | xargs --no-run-if-empty rm
ls -trは、すべてのファイルを最も古いものから最初に表示します(-t最新のものから、-r逆順)。
head -n -5は、最後の5行を除くすべて(つまり、最新の5ファイル)を表示します。
xargs rmは、選択されたファイルごとにrmを呼び出します。
-1
は、出力がパイプラインに対するものである場合のデフォルトであるため、ここでは必須ではありません。これにはxargs
、スペース、引用符、&cを使用して名前を解析するときのデフォルトの動作に関連するはるかに大きな問題があります。
--no-run-if-empty
私のシェルでは認識されないようです。WindowsでCmderを使用しています。
find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f
-printfにはGNU find、-zにはGNU sort、「\ 0」にはGNU awk、そして-0にはGNU xargsが必要ですが、改行またはスペースが埋め込まれたファイルを処理します。
awk
ロジックの複雑さ(さらに言えば、必要性)に驚いています。OPの質問に必要ないくつかの要件がありませんか?
while read -r -d ' '; IFS= -r -d ''; do ...
ループにパイプすることです-最初の読み取りはスペースで終了し、2番目の読み取りはNULに進みます。
sed -z -e 's/[^ ]* //; 1,5d'
が最も明確だと思います。(または多分sed -n -z -e 's/[^ ]* //; 6,$p'
。
現在のディレクトリにディレクトリがある場合、これらすべての回答は失敗します。これが機能するものです:
find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm
この:
現在のディレクトリにディレクトリがある場合に機能します
以前のファイルを削除できなかった場合でも(権限などにより)、各ファイルを削除しようとします。
現在のディレクトリにあるファイルの数が多すぎて、xargs
通常は失敗する場合、安全に失敗します(-x
)
ファイル名にスペースが含まれていない(おそらく間違ったOSを使用しているのでしょうか?)
find
単一のコマンドラインで渡されるよりも多くのファイル名を返すとどうなりますls -t
か?(ヒント:の実行は複数回行わls -t
れ、グローバルに正しい並べ替え順序ではなく、個別に並べ替えられます。したがって、十分に大きなディレクトリで実行すると、この回答は大きく崩れます)。
ls -tQ | tail -n+4 | xargs rm
変更時刻ごとにファイル名をリストし、各ファイル名を引用します。最初の3つを除外します(最新の3つ)。残りを削除します。
mklement0からの役立つコメントの後に編集(ありがとう!):-n + 3引数を修正しました。ファイル名に改行が含まれている場合や、ディレクトリにサブディレクトリが含まれている場合、これは期待どおりに機能しないことに注意してください。
-Q
オプションでは、私のマシン上に存在していないようです。
-Q
。はい、-Q
GNU拡張機能です(POSIX ls
仕様はこちら)。小さな注意点(実際にはまれな問題):-Q
埋め込まれたエンコード改行をリテラルとしてファイル名に\n
、rm
認識されません。最初の3つを除外するには、xargs
引数を指定する必要があり+4
ます。最後に、他のほとんどの回答にも適用される警告:コマンドは、現在のディレクトリにサブディレクトリがない場合にのみ、意図したとおりに機能します。
--no-run-if-empty
オプション:ls -tQ | tail -n+4 | xargs --no-run-if-empty rm
改行を無視すると、セキュリティと適切なコーディングが無視されます。wnoiseが唯一の良い答えでした。これは、ファイル名を配列$ xに配置するバリエーションです。
while IFS= read -rd ''; do
x+=("${REPLY#* }");
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )
IFS
します。そうしないと、ファイル名の末尾の空白が失われる危険性があります。缶スコープそのリードコマンドに:while IFS= read -rd ''; do
"${REPLY#* }"
?
ファイル名にスペースがない場合、これは機能します:
ls -C1 -t| awk 'NR>5'|xargs rm
ファイル名にスペースがある場合、次のようなもの
ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh
基本的なロジック:
while read
スペースを扱うための秘訣を忘れないでください: ls -C1 -t | awk 'NR>5' | while read d ; do rm -rvf "$d" ; done
while IFS= read -r d
は少し良いでしょう-は-r
によってバックスラッシュリテラルが消費されるread
のをIFS=
防ぎ、は末尾の空白の自動トリミングを防ぎます。
touch $'hello \'$(rm -rf ~)\' world'
。ファイル名内のリテラル引用符は、追加するリテラル引用符に対抗するためsed
、ファイル名内のコードが実行されます。
| sh
は、シェルインジェクションの脆弱性があるフォームを指していました)。
zshを使用
現在のディレクトリを気にせず、999個を超えるファイルはないと仮定します(必要に応じて、より大きな数を選択するか、whileループを作成します)。
[ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999])
では*(.om[6,999])
、.
平均ファイル、o
平均ソート順m
、変更日による平均(a
アクセス時間またはc
inode変更のために入力)は、[6,999]
ファイルの範囲を選択するため、最初に5をrmしません。
om
)を機能させることができませんでした(私が試した並べ替えは効果がありませんでした-OSX 10.11.2(zsh 5.0.8および5.1.1で試した場合) 、Ubuntu 14.04(zsh 5.0.2)でも)-何が欠けていますか?範囲のエンドポイントについては、ハードコーディングする必要はありません。-1
最後のエントリを参照し、残りのすべてのファイルを含めるために使用するだけです[6,-1]
。
私はこれが古いスレッドであることを理解していますが、誰かがこれから利益を得るでしょう。このコマンドは、現在のディレクトリでファイルを検索します。
for F in $(find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n' | sort -r -z -n | tail -n+5 | awk '{ print $2; }'); do rm $F; done
これは、検索ドメインを式に一致するファイルに制限できるため、以前のいくつかの回答よりも少し堅牢です。まず、必要な条件に一致するファイルを見つけます。それらの横にタイムスタンプが付いたファイルを印刷します。
find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n'
次に、タイムスタンプで並べ替えます。
sort -r -z -n
次に、リストから最新の4つのファイルを削除します。
tail -n+5
2番目の列(タイムスタンプではなくファイル名)を取得します。
awk '{ print $2; }'
次に、全体をforステートメントにまとめます。
for F in $(); do rm $F; done
これはもっと冗長なコマンドかもしれませんが、条件付きファイルをターゲットにして、それらに対してより複雑なコマンドを実行できる方がはるかに幸運でした。
Sed-Onlinersで興味深いcmdを見つけました-最後の3行を削除します-猫の皮を剥ぐ別の方法に最適です(そうではありません)。
#!/bin/bash
# sed cmd chng #2 to value file wish to retain
cd /opt/depot
ls -1 MyMintFiles*.zip > BigList
sed -n -e :a -e '1,2!{P;N;D;};N;ba' BigList > DeList
for i in `cat DeList`
do
echo "Deleted $i"
rm -f $i
#echo "File(s) gonzo "
#read junk
done
exit 0
10個の最新(最新)ファイルを除くすべてを削除します
ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm
10ファイル未満の場合、ファイルは削除されず、次のエラーが発生します。エラーヘッド:不正な行数-0
私はbusybox(ルーター)のためのエレガントなソリューションを必要としていましたが、すべてのxargsまたは配列ソリューションは役に立たなかった-そのようなコマンドはそこにはありませんでした。10項目について話しているため、find and mtimeは適切な答えではありません。必ずしも10日間とは限りません。Espoの答えは、最も短く、最もクリーンで、おそらく最も逆の答えでした。
スペースのエラーとファイルを削除しない場合は、どちらも標準的な方法で簡単に解決できます。
rm "$(ls -td *.tar | awk 'NR>7')" 2>&-
もう少し教育的なバージョン:awkを別の方法で使用すれば、すべてを実行できます。通常、私はこのメソッドを使用して、awkからshに変数を渡します(返す)。私たちはできないことをいつも読んでいるので、私は違うことを頼みました:ここに方法があります
ファイル名のスペースに関して問題のない.tarファイルの例。テストするには、「rm」を「ls」に置き換えます。
eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')
説明:
ls -td *.tar
時間順にソートされたすべての.tarファイルをリストします。現在のフォルダ内のすべてのファイルに適用するには、「d * .tar」の部分を削除します
awk 'NR>7...
最初の7行をスキップします
print "rm \"" $0 "\""
次の行を作成します:rm "file name"
eval
それを実行する
をrm
使用しているため、上記のコマンドはスクリプトでは使用しません。より賢い使い方は:
(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))
使用する場合はls -t
次のコマンドをこのような愚かな例に害しないだろうtouch 'foo " bar'
としtouch 'hello * world'
。実際にそのような名前のファイルを作成することはありません!
サイドノート。この方法で変数をshに渡したい場合は、単純に出力を変更します(単純な形式で、スペースは許容されません)。
print "VarName="$1
変数VarName
をの値に設定します$1
。複数の変数を一度に作成できます。これVarName
は通常のsh変数になり、通常は後でスクリプトまたはシェルで使用できます。したがって、awkで変数を作成してシェルに戻すには、次のようにします。
eval $(ls -td *.tar | awk 'NR>7 { print "VarName=\""$1"\"" }'); echo "$VarName"
leaveCount=5
fileCount=$(ls -1 *.log | wc -l)
tailCount=$((fileCount - leaveCount))
# avoid negative tail argument
[[ $tailCount < 0 ]] && tailCount=0
ls -t *.log | tail -$tailCount | xargs rm -f
xargs
なし、-0
または最低で-d $'\n'
も信頼できません。名前にスペースまたは引用符が含まれるファイルでこれがどのように動作するかを確認します。
これをbashシェルスクリプトにしました。使用法:keep NUM DIR
ここで、NUMは保持するファイルの数、DIRはスクラブするディレクトリです。
#!/bin/bash
# Keep last N files by date.
# Usage: keep NUMBER DIRECTORY
echo ""
if [ $# -lt 2 ]; then
echo "Usage: $0 NUMFILES DIR"
echo "Keep last N newest files."
exit 1
fi
if [ ! -e $2 ]; then
echo "ERROR: directory '$1' does not exist"
exit 1
fi
if [ ! -d $2 ]; then
echo "ERROR: '$1' is not a directory"
exit 1
fi
pushd $2 > /dev/null
ls -tp | grep -v '/' | tail -n +"$1" | xargs -I {} rm -- {}
popd > /dev/null
echo "Done. Kept $1 most recent files in $2."
ls $2|wc -l