不明な番号を含むファイルを削除する方法は?

不明な番号を含むファイルを削除する方法は?

次の名前のファイルを作成するコードがあります。

body00123.txt
body00124.txt
body00125.txt

body-1-2126.txt
body-1-2127.txt
body-1-2128.txt

body-3-3129.txt
body-3-3130.txt
body-3-3131.txt

これにより、ファイルの最初の2つの数字は「負の数」になりますが、最後の3つの数字はそうではありません。

次のリストがあります。

123
127
129

これらの数字の1つで終わらないすべてのファイルを削除したいと思います。必要な残りのファイルの例は次のとおりです。

body00123.txt

body-1-2127.txt

body-3-3129.txt

私のコードはPythonで実行されているので、次のことを試しました。

for i not in myList:
     os.system('rm body*' + str(i) + '.txt')

これにより、すべてのファイルが削除されます。

答え1

時には、「良い」ファイルを別の場所に移動し、悪いファイルを削除してから、良いファイルを再度移動する方が簡単です。

方法が適切であれば、これがうまくいく可能性があります。

#!/bin/sh

# Temporary directory to hold the files we want to keep
mkdir .keep || exit

for a in $(cat keeplist)
do
  # These are the files we want to keep
  mv body*$a.txt .keep

  # Except this might match negative versions, so remove them
  rm -f .keep/*-$a.txt
done

# Remove the files we don't want
rm body*

# Move the good files back
mv .keep/* .

# Tidy up
rmdir .keep

たとえば、次のように起動した場合:

% ls
body-1-2126.txt  body-2-3-123.txt  body-3-3131.txt  body00125.txt  s
body-1-2127.txt  body-3-3129.txt   body00123.txt    fix
body-1-2128.txt  body-3-3130.txt   body00124.txt    keeplist

その後、私たちが終わるスクリプトを実行します

% ls
body-1-2127.txt  body-3-3129.txt  body00123.txt  fix  keeplist  s

答え2

存在するzsh

$ set -o extendedglob
$ list=(123 127 129)
$ echo rm body(^*(${(~j[|])list})).txt
rm body00124.txt body00125.txt body-1-2126.txt body-1-2128.txt body-3-3130.txt body-3-3131.txt

echo実際に実行されたタスクを削除)

パラメータj[|]拡張フラグはj要素を連結します。このフラグを使用すると、グローバル演算子(リテラルではなく代替演算子)として解釈されます。$list|~|

したがって、globは否定演算子としてbody(^*(123|127|129)).txtで終わるので、一致するファイル名はで始まり、その後に123、127、129で終わらない文字列が続きます。^extendedglobbody.txt

追加の条件が必要な場合は、次のよう*に置き換えてください。この数字の前の部分を維持するには、この数字の前の部分はこの数字で終わることができないため、exampleという名前のファイルも削除されます。(^*-)-body-1-1-123.txt

より厳密なマッチングのために、次のこともできます。

n='((-|)[0-9])' # digit with an optional - sign
echo rm body$~n(#c2)($~n(#c3)~(${(~j[|])list})).txt

ここでは(#c2)、反復演算子、~例外(および非)演算子です。内容がリテラル文字列ではなくパターンとして解釈されることを除いて、$~n同様です(上記のパラメータ拡張フラグと同様)。$n$n~

したがって、私たちはbody2つの数字の後ろに来るものと一致します。各数字は、-オプションで、前に 1 つと後に 3 つの数字が続きます。ただし、 のメンバーのひとつである数字は除き、その$listあとに数字が続きます.txt

答え3

find名前と一致しないファイル名または名前リストと一致しないファイルの操作を許可するために無効にできる名前一致基本要素があります。

findデフォルトは複数のタスクを1行にまとめることであるため、次のスクリプトをand作成できますbash

#!/usr/bin/env bash

list=( 123 127 129 )

findcmd="find . -type f $(printf -- ' -not -name \*%s.txt' "${list[@]}")"

bash -v <<< "$findcmd"

(注:このbash行は次のようにすることもできます。

printf '%s\n' "$findcmd"
eval $findcmd

)

このスクリプトの出力は次のとおりです。

find . -type f  -not -name \*123.txt -not -name \*127.txt -not -name \*129.txt
./body-3-3130.txt
./body00125.txt
./body-1-2126.txt
./body00124.txt
./body-1-2128.txt
./body-3-3131.txt

ここには2つの情報があります。findアーカイブする数値配列で作成されたコマンド構文と、その数値と一致しない結果ファイルのリスト。

ファイル名のリストをもう一度確認してください。これらのファイルをすべて削除することを確認したら、findコマンド構文をコピーして以下のようにfind作業指示書と貼り付けます(読みやすくするためにバックスラッシュでエスケープされた改行を使用して表示します)。-exec rm -v {} \;

$ find . -type f  -not -name \*123.txt -not -name \*127.txt -not -name \*129.txt \
    -exec rm -v {} \;
./body-3-3130.txt
./body00125.txt
./body-1-2126.txt
./body00124.txt
./body-1-2128.txt
./body-3-3131.txt

答え4

Python。直接的な方法

import os
import glob

num_lst = [123, 127, 129]
num_as_str_set = set(map(str, num_lst))

# If not other files except .txt in directory, listdir() will be enough
#for filename in os.listdir():
for filename in glob.glob("*.txt"):
    #7654321
    #123.txt
    #[-7:-4] -> 123
    if filename[-7:-4] not in num_as_str_set:
        print("remove", filename)
# Uncomment to remove files
#       os.remove(filename)

Bashでも同じ論理

declare -A hash_map
hash_map=( [123]= [127]= [129]= )

for fn in *.txt; do
    key="${fn: -7:-4}"
    if ! [[ -v hash_map["$key"] ]]; then
        echo "$fn"
#Uncomment to actual remove
#       rm -v "$fn"
    fi  
done

Python。難しいですが、おそらく最善の方法ではありません。(ベンチマークが必要)

import os
from glob import glob
from itertools import chain

num_lst = [123, 127, 129]
s = set(glob("*.txt")) - set(chain(*(glob(f"*{num}.txt") for num in num_lst)))
#Uncomment to remove files
#list(map(os.remove, s))

関連情報