
多数のサブフォルダを含むフォルダがあります。各サブフォルダから小さいファイルをすべて削除し、最大のファイルのみを残したいです。
例えば:
Subfolder1
---------- File 1 ---- 300k
---------- File 2 ---- 299k
---------- File 3 ---- 800k
800kのみfile 3
残す必要があります。フォルダーにファイルが 1 つしかない場合は、それが残ります。
このコードは機能しますが、for loop(ディレクトリの再帰の場合)に入れることはできません。
find . -type f -maxdepth 1 | sort -n -r | tail -n +2 | xargs -I{} rm -v {}
これどうやってするの?
答え1
~$ tree -fQFi --sort=size pluto
"pluto"
"pluto/pluto1"/
"pluto/pluto1/pluto3"/
"pluto/pluto1/pluto3/nozero.txt"
"pluto/pluto1/pluto3/zero ed.txt"
"pluto/pluto1/nozero.txt"
"pluto/pluto2"/
"pluto/pluto2/nozero.txt"
"pluto/pluto2/nozer.txt"
"pluto/pluto2/zero.txt"
"pluto/pluto4"/
"pluto/pluto4/zeroed.txt"
"pluto/zeroed.txt"
4 directories, 8 files
~$ tree -fQFic --noreport --sort=size pluto | \
> awk -F"/" 'NR==1||/\/$/{next}; \
> {path=""; for(i=1;i<NF;i++) path=path$i; if(a[path]++) print}'
"pluto/pluto1/pluto3/zero ed.txt"
"pluto/pluto2/nozer.txt"
"pluto/pluto2/zero.txt"
~$ tree -fQFic --noreport --sort=size pluto | \
> awk -F"/" 'NR==1||/\/$/{next}; \
> {path=""; for(i=1;i<NF;i++) path=path$i; if(a[path]++) print}' | \
> xargs rm -v
'pluto/pluto1/pluto3/zero ed.txt' rimosso
'pluto/pluto2/nozer.txt' rimosso
'pluto/pluto2/zero.txt' rimosso
~$ tree -fQFi --sort=size pluto
"pluto"
"pluto/pluto1"/
"pluto/pluto1/pluto3"/
"pluto/pluto1/pluto3/nozero.txt"
"pluto/pluto1/nozero.txt"
"pluto/pluto2"/
"pluto/pluto2/nozero.txt"
"pluto/pluto4"/
"pluto/pluto4/zeroed.txt"
"pluto/zeroed.txt"
4 directories, 5 files
tree
ディレクトリごとにリストし、次に下降サイズでリストします。
awk
コードスキップの1行目tree
の出力1行目または末尾にスラッシュがある行(ディレクトリなど)awk
の 2 行目のコードでは、フル パス (ループ) から dirname を構築しfor
、前の行で dirname が 1 回検出された場合に限り、フル パス名を出力します (つまり、各ディレクトリについて、2 番目にリストされたファイルから出力します)。
答え2
正当化
これは、一緒に機能するコマンドを構築する私の試みですどれでもディレクトリとファイル名。一般に Linux のパス (およびファイルシステム内の名前) には、ヌル ( 0x00
) とを除く任意の文字を含めることができます。問題となる文字は、" " (スペース)、その他の空白文字、、、改行、その他の印刷できない文字/
です。したがって、次の点に注意してください。'
"
- 一部の文字を他の文字に置き換えるツールを廃止します (例: の多くの実装では、印刷できない文字
ls
が印刷されます)。?
- すべての名前をヌル終端文字列として渡します(それらを解析できるツールを選択します)。
- 適切に引用してください。
私は以下の議論に触発されましたこの他の答え。
実際のコマンド
テストバージョンでは、ls
削除されるファイルのみが対象となります:
find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n\0" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r ls -l
ls
はい、先ほど言ったにもかかわらず、ここではを使用しています。これはls
、出力がそれ以上解析されないためです。結果を表示するためだけに使用しています。名前に問題のある文字を含むディレクトリやファイルがある場合は、ls
その動作を観察して、解析しないls
(あなたが絶対に安全であることを知っていない限り)ls
。
テストバージョンを理解する(説明については下記をご覧ください)実際に動作するバージョンを公開する前に試してみましょう(すぐ下)ファイルを削除します。私はインターネット上のただの一般人だということを忘れないでください。
動作バージョンでは、ファイルが削除されます:
find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n\0" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r rm
説明
これはテストバージョンを複数行に分割したものです(ただし、1行のままですbash
。このトリックインラインコメントへ):
find -type d -exec `# Find all directories under (and including) the current one.` \
sh -c ' `# In every directory separately...` \
find "$0" -maxdepth 1 -mindepth 1 -type f -exec `# ...find all files,...` \
stat --printf "%s %n\0" \{\} + | # ...get their sizes and names,...
sort -znr | # ...sort by size...
tail -zn +2' `# ...and discard the "biggest" entry.` \
{} \
\; | # (All the directories have been processed).
cut -zf 2- -d " " | # Then extract filenames...
xargs -0r ls -l # ...and ls them (rm in the working version).
使用された技術、克服された障害:
- 文字列を解析するツールは、ヌルで終了する文字列で動作するように指示されます。
stat --printf "…\0"
;sort -z
、、;tail -z
cut -z
xargs -0 …
;find -print0
(この例では必要ありませんが、一般的には非常に一般的です。したがって、とにかく言及します)。
sh -c '…'
内部のパイプを使用する方法ですfind -exec
。find -type d -exec sh -c 'find "{}" …
ディレクトリ名に「"
;」が含まれている場合は中断されますがfind -type d -exec sh -c 'find "$0" … ' {} \;
、正常に動作します。{}
内側の声明では、外側がそれらを置換するのを防ぐためにfind
逃げられます( ) 。\{\}
find
cut
すぐに続くとtail
、cut
ディレクトリごとに1find
つを実行します。cut
- のオプションは、 への入力がない場合に(作業バージョンでは) が実行されないよう
-r
にxargs
します。ls
rm
xargs