我有一些代码可以写出名称如下的文件:
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
这样文件中的前两个数字可以是“负数”,但最后 3 个数字则不能。
我有一个这样的列表:
123
127
129
我想删除所有不以这些数字之一结尾的文件。所需的剩余文件的示例如下:
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[|]
扩展标志连接withj
的元素。使用该标志,它们被解释为全局运算符(交替运算符而不是文字)。$list
|
~
|
因此 glob 最终成为body(^*(123|127|129)).txt
,^
作为否定extendedglob
运算符,因此匹配以 开头的文件名body
,后跟任何不以 123、127、129 结尾的字符串,最后是.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)
是重复运算符,and~
是例外(与非)运算符。$~n
类似于$n
,除了 的内容$n
被解释为模式而不是文字字符串(如~
上面的参数扩展标志)。
因此,我们匹配 onbody
后跟两个数字,每个-
数字可选地前面跟一个 ,后跟 3 个数字,除了那些是 的成员之一$list
,后跟 的数字之外.txt
。
答案3
find
有一个名称匹配原语,可以将其取反以允许对不匹配名称或不匹配任何名称列表的文件执行操作。
由于find
默认是将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
这里我们看到两条信息: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))