我有一个数据集,其中包含名为“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 给出的列表进行排序和打印(在验证文件存在之后),所以基本上是多余的。您也可以使用print
或printf
实用程序来打印该列表。
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].txt
will 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[@]}"