如何列出特定数字范围内包含奇数的所有文件?

如何列出特定数字范围内包含奇数的所有文件?

我有一个数据集,其中包含名为“file000.txt”到“file999.txt”的文件。

我知道我可以使用通配符列出从 0 到 500 的所有文件

ls file{0..500}

我可以找到包含奇数的所有文件

ls file*[1,3,5,7,9].txt

我是 Unix 新手,所以我不确定如何组合这两个参数来列出包含 0-500 范围内的奇数的所有文件,最好在一行中。我确信这必须是一个快速的解决方案,但我无法弄清楚。

答案1

zsh

set -o extendedglob
print -rC1 -- file(<0-500>~^*[13579]).txt

请注意,{0..50}(来自 zsh)不是 glob 运算符,它是大括号扩展,无论相应文件是否存在都会扩展。

<0-500>(zsh 特有,尚未被其他 shell 复制)是一个 glob 运算符,它匹配组成 0 到 500 之间数字的 ASCII 十进制数字序列。

~是个除了并不是) 运算符并且^不是A~B是 A并不是BA~^B是 A不是不是B,所以A和B。

在 中bash,您可以执行类似的操作:

shopt -s failglob extglob
printf '%s\n' file*(0)[01234][0123456789][13579].txt

*(0)(ksh 的扩展 glob 运算符)是任意数量的零,然后我们期望一个从 0 到 4 的数字,一个从 0 到 9 的数字,以及 13579 集中的一个数字。

现在,无论 zsh 和 bash(以及 ksh931 和yash -o braceexpand)中是否存在相应文件,大括号扩展都扩展为 1, 3, ... 499:

printf '%s\n' file{1..499..2}.txt

其他注意事项:

  • [1,3,5,7,9]与 相同[13579,],它匹配该集合中的任一字符。你想要[13579]代替。
  • 在 bash(不是 zsh)中,[0-9]通常匹配数百个不同的类似数字的字符。[0123456789]如果您只想匹配这 10 个 ASCII 十进制字符,请使用此选项。
  • 请注意,如果您不传递该-d选项,ls并且传递给它的任何filexxx.txt文件(由于 shell 的大括号扩展或文件名生成(又名通配符)的结果)都是类型目录ls将列出目录的内容。当-d将全局扩展传递给ls.不过,在这里,ls最终只会对 shell 给出的列表进行排序和打印(在验证文件存在之后),所以基本上是多余的。您也可以使用printprintf实用程序来打印该列表。

1{a,b}大括号扩展来自 70 年代末的 csh,zsh在 90 年代初用{4..10}(and {a-zXYZ}with braceccl) 对其进行了扩展),ksh93 将其扩展{1..400..2%04d}至 2000 年代中期,该{1..400.2}部分又回到了其他 shell,该%04d部分仍然特定于ksh93 AFAIK,尽管 zsh{0001..0400}从一开始就这样做,并且通常有单独的填充运算符。

答案2

用于ls file[0-4][0-9][13579].txt列出以奇数结尾的文件,用于ls file[0-4][13579][0-9].txt列出中间为奇数的文件。

最终 ls 是

 ls file[0-4][0-9][13579].txt file[0-4][13579][0-9].txt file[13][0-9][0-9].txt

请注意file*[1,3,5,7,9].txtwill match file100001.txt,这不是您想要的。另外,您不要将逗号作为括号内的分隔符:[1,3,5,7,9]与逗号相同[13579,]并且也匹配逗号。

答案3

正如其他答案所示,找到奇数很容易。主要是因为看最后一位数字就足够了

对于更一般的情况,您需要将数字实际视为数字。直接做到这一点很难,但是您可以例如循环遍历更大的集合,并在数组中选择匹配的集合。 shell(尤其是 Bash)做这类事情的速度不是太快,但对于数百或数千个文件来说它可能会做。

这里是数字可被七整除的文件(在 Bash 中,这将匹配带有前导零的数字):

shopt -s extglob
a=()
for f in file+([0-9]).txt; do 
    n=${f##file*(0)}      # remove leading 'file' and any zeroes
    n=${n%.txt}           # remove tailing '.txt'
    if (( $n % 7 == 0 )); then
        a+=("$f")
    fi
done
printf "%s\n" "${a[@]}"    # or whatever you do with the filenames

或者,您可以先循环数字(Bash 和其他,这不会看到带有前导零的数字):

a=()
for ((i = 0; i <= 500; i += 7)); do
    f="file${i}.txt"
    if [[ -e "$f" ]]; then
        a+=("$f")
    fi
done
printf "%s\n" "${a[@]}"

或者,通过大括号扩展生成一堆字符串,这些字符串被人为地制成球体,这样 shell 就可以被告知删除不匹配的字符串。 ([f]这里是一个应该只匹配字母的模式f,也许)这有点暴力,可能效率不那么高,但它更紧​​凑,因为它不使用显式循环。 (Bash 也看不到带有前导零的数字。)

shopt -s nullglob   # remove non-matching globs
shopt -u failglob
a=([f]ile{0..499..7}.txt)
printf "%s\n" "${a[@]}"

相关内容