如何删除其中包含未知编号的文件?

如何删除其中包含未知编号的文件?

我有一些代码可以写出名称如下的文件:

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))

相关内容