Filename.12345.endという形式の数千のファイルがあります。私は12番目ごとのファイルのみを保持したいので、file.00012.end、file.00024.end ... file.99996.endをすべて削除します。
ファイルには、ファイル名の前に番号が付いている場合があり、通常は次の形式です:file.00064.name.99999.end
私はBash Shellを使用していますが、ファイルをループ処理する方法がわからず、数値を取得して、number%%12=0
でファイルを削除しているかどうかを確認できません。誰も私を助けることができますか?
ありがとう、ドリナ
これがPerlソリューションです。これは、数千のファイルに対してはるかに高速です。
Perl -e '@bad=grep{/(\d+)\.end/ && $1 % 12 != 0}@ARGV; unlink @bad' *
さらに次のように凝縮できます。
Perl -e 'unlink grep{/(\d+)\.end/ && $1 % 12 != 0}@ARGV;' *
ファイルが多すぎて、単純な*
を使用できない場合、次のようなことができます。
Perl -e 'opendir($d,"."); unlink grep{/(\d+)\.end/ && $1 % 12 != 0} readdir($dir)'
速度に関しては、このアプローチと、他の回答の1つで提供されているシェルの比較です。
$ touch file.{01..64}.name.{00001..01000}.end
$ ls | wc
64000 64000 1472000
$ time for f in ./* ; do file="${f%.*}"; if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then rm "$f"; fi; done
real 2m44.258s
user 0m9.183s
sys 1m7.647s
$ touch file.{01..64}.name.{00001..01000}.end
$ time Perl -e 'unlink grep{/(\d+)\.end/ && $1 % 12 != 0}@ARGV;' *
real 0m0.610s
user 0m0.317s
sys 0m0.290s
ご覧のとおり、違いは非常に大きく、 予想どおり です。
-e
は、単にPerl
にコマンドラインで指定されたスクリプトを実行するように指示しています。@ARGV
は、スクリプトに指定されたすべての引数を含む特別な変数です。 *
を指定しているため、現在のディレクトリ内のすべてのファイル(およびディレクトリ)が含まれます。grep
は、ファイル名のリストを検索し、数字の文字列、ドット、およびend
(/(\d+)\.end/)
に一致するものを探します。
番号(\d
)はキャプチャグループ(括弧)にあるため、$1
として保存されます。そのため、grep
はその数が12の倍数であるかどうかをチェックし、そうでない場合はファイル名が返されます。つまり、配列@bad
には、削除するファイルのリストが保持されます。
次に、リストはunlink()
に渡され、ファイル(ディレクトリは除く)が削除されます。
ファイル名の形式がfile.00064.name.99999.end
である場合、最初に番号以外をすべて削除する必要があります。これを行うには、for
ループを使用します。
また、Bash算術では0で始まる数字を基数8として扱うため、基数10を使用するようにBashシェルに指示する必要があります。
スクリプトとして、ファイルを含むディレクトリで起動するには、次を使用します。
#!/bin/bash
for f in ./*
do
if [[ -f "$f" ]]; then
file="${f%.*}"
if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then
rm "$f"
fi
else
echo "$f is not a file, skipping."
fi
done
または、この非常に長いいコマンドを使用して同じことを行うことができます。
for f in ./* ; do if [[ -f "$f" ]]; then file="${f%.*}"; if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then rm "$f"; fi; else echo "$f is not a file, skipping."; fi; done
すべての部分を説明するには:
for f in ./*
は、現在のディレクトリ内のすべてのものを意味します。do....これにより、見つかった各ファイルまたはディレクトリが変数$ fとして設定されます。if [[ -f "$f" ]]
は、見つかったアイテムがファイルかどうかを確認します。ファイルでない場合は、echo "$f is not...
部分にスキップします。これは、誤ってディレクトリを削除し始めないことを意味します。file="${f%.*}"
は、$ file変数を、最後の.
の後にあるファイル名を切り取るように設定します。if [[ $((10#${file##*.} % 12)) -eq 0 ]]
は、メインの算術演算が開始される場所です。${file##*.}
は、拡張子のないファイル名の最後の.
の前のすべてをトリミングします。 $(( $num % $num2 ))
はモジュロ演算を使用するためのBash算術の構文です。開始時の10#
は、Bashに10を基数として使用し、厄介な先行0を処理するように指示します。 $((10#${file##*.} % 12))
は、ファイル名番号の残りを12で割った残りを残します。-ne 0
は、残りがゼロに等しくないかどうかをチェックします。rm
コマンドで削除されます。これを最初に実行するときに、rm
をecho
に置き換えて、期待どおりになっていることを確認できます。削除するファイル。このソリューションは非再帰的です。つまり、現在のディレクトリ内のファイルのみを処理し、サブディレクトリには一切入りません。
ディレクトリについて警告するif
コマンドを伴うecho
ステートメントは、それ自体ではrm
がディレクトリについて文句を言い、それらを削除しないため、実際には必要ありません。
#!/bin/bash
for f in ./*
do
file="${f%.*}"
if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then
rm "$f"
fi
done
または
for f in ./* ; do file="${f%.*}"; if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then rm "$f"; fi; done
正しく動作します。
Bashブラケット拡張を使用して、12番目ごとの番号を含む名前を生成できます。テストデータを作成しましょう
$ touch file.{0..9}{0..9}{0..9}{0..9}{0..9}.end # create test data
$ mv file.00024.end file.00024.end.name.99999.end # testing this form of filenames
次に、以下を使用できます
$ ls 'file.'{00012..100..12}* # print these with numbers less than 100
file.00012.end file.00036.end file.00060.end file.00084.end
file.00024.end.name.99999.end file.00048.end file.00072.end file.00096.end
$ rm 'file.'{00012..100000..12}* # do the job
しかし、大量のファイルでは動作が絶望的に遅くなります-数千の名前を生成するのに時間とメモリが必要です-したがって、実際の効率的なソリューションよりもトリックです。
少し長いですが、私の頭に浮かんだものです。
for num in $(seq 1 1 11) ; do
for sequence in $(seq -f %05g $num 12 99999) ; do
rm file.$sequence.end.99999;
done
done
説明:12回ごとに11回ファイルを削除します。
すべての謙虚さで、私はこの解決策が他の答えよりもはるかに優れていると思います:
find . -name '*.end' -depth 1 | awk 'NR%12 != 0 {print}' | xargs -n100 rm
簡単な説明:最初に、find
でファイルのリストを生成します。名前が.end
で終わり、深さが1のすべてのファイルを取得します(つまり、サブフォルダーではなく、作業ディレクトリに直接あります。ない場合は省略できます)サブフォルダー)。出力リストはアルファベット順にソートされます。
次に、そのリストをawk
にパイプします。ここでは、行番号である特別な変数NR
を使用します。 NR%12 != 0
の場所にあるファイルを印刷して、12番目ごとのファイルを除外します。モジュロ演算子の結果はブール値として解釈され、awk 'NR%12'
は暗黙的に行われるため、awk
コマンドは{print}
に短縮できます。
これで、削除する必要があるファイルのリストができました。これは、xargsとrmで実行できます。 xargs
は、引数として標準入力を使用して、指定されたコマンド(rm
)を実行します。
多くのファイルがある場合、「引数リストが長すぎます」などのエラーが表示されます(私のマシンでは、その制限は256 kBであり、POSIXで必要な最小値は4096バイトです)。これは-n 100
フラグによって回避できます。これは、引数を100ワードごとに分割し(行ではなく、ファイル名にスペースがある場合に注意するもの)、それぞれrm
コマンドを実行します。引数は100個のみです。
Bashのみを使用する場合、私の最初のアプローチは次のとおりです。その後、保存した12の倍数のファイルを元の場所に戻します。そのため、次のように動作します。
cd dir_containing_files
mkdir keep_these_files
n=0
while [ "${n}" -lt 99999 ]; do
padded_n="`echo -n "00000${n}" | tail -c 5`"
mv "filename${padded_n}.end" keep_these_files/
n=$[n+12]
done
rm filename*.end
mv keep_these_files/* .
rmdir keep_these_files