次の名前のファイルを作成するコードがあります。
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で終わらない文字列が続きます。^
extendedglob
body
.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
~
したがって、私たちはbody
2つの数字の後ろに来るものと一致します。各数字は、-
オプションで、前に 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))