我通过以下方式对某些数字之间的文件进行计数:
for i in $(eval echo {$start..$stop}); do
ls /home/me/*/file_$i.txt 2> /dev/null | wc -l
done
$(eval echo {$start..$stop})
是必要的,因为序列表达式不能接受一般输入。
然而,这并没有达到我想要的效果,因为对于循环的每次迭代,它都会计算该单个文件是否存在。相反,我希望它迭代所有文件,但计算 $start 和 $stop 之间存在的总数(请记住,我正在处理大量文件并试图避免溢出)。我如何修改上面的方案来计算现有文件的总数,而不是检查每个迭代文件是否存在?
答案1
尝试这个,
Count=0
for i in {$start..$stop}; do
Counter="$(ls /home/me/*/file_$i.txt 2> /dev/null | wc -l)"
Count=$((Count+Counter))
done
echo "$Count"
答案2
根本不要使用循环。相反,列出全部grep
文件并使用或 之类的命令akw
过滤掉start
和之间的文件stop
。printf
不会有问题,Argument list too long
因为它是bash
内置的。
printf '%s\0' /home/me/*/file_*.txt |
grep -cEzf <(seq "$start" "$stop" | sed 's/.*/_&\.txt$/')
grep
这里我们假设您有可以处理空字节的 GNU 版本。这对于安全地处理路径很重要。空字节是唯一不能成为路径一部分的字符,因此我们可以使用它来分隔路径。
如果您没有 GNU 版本,则可以创建一些在假设没有路径包含换行符的情况下通过换行符分隔路径的内容。替换\0
为\n
并删除-z
标志。
如果您更改…_*.txt
为其他内容,您可能还需要更新sed
命令。
答案3
eval
除非您没有其他选择,否则不要使用。它是否存在潜在危险,具体取决于$start
和变量的内容$stop
。养成在不必要的地方使用它的习惯并不是一个好主意。ls
除了在终端中查看之外,不要将 的输出用于任何其他用途。即使以最简单的方式尝试解析ls
,也可能会以各种方式失败。使用任何事物可以生成一个以 NUL 分隔的文件名列表:find -print0
通常是一个不错的选择。bash 内置
{x..y}
序列扩展无法计算变量,但独立seq
程序可以将变量作为参数。它还具有一个有用的-s
选项来指定分隔符。
以下需要一个grep
支持-z
NUL 分隔输入记录选项的版本(例如 GNU、FreeBSD 和大多数其他现代的的版本grep
)。
# build a regular expression matching the desired sequence
re="$(seq -s '|' "$start" "$stop")"
# use the RE with `find ... -print0` and `grep -E -z -c`
find /home/me/*/ -maxdepth 1 -type f -name 'file_*.txt' -print0 |
grep -Ezc "/file_($re)\.txt$"
例如,如果start=3
和stop=7
,则将$re
是3|4|5|6|7
。该grep
命令随后将扩展为:
grep -Ezc "/file_(3|4|5|6|7)\.txt$"
顺便说一句,命令-name 'file_*.txt'
的参数find
不是必需的。它们可以被删除,管道仍然会运行而不会出现错误。他们所做的只是减少需要处理的输入数据grep
。充其量只是一个非常小的优化。
例如
find /home/me/*/ -maxdepth 1 -type f -print0 | grep -Ezc "/file_($re)\.txt$"
答案4
如果我理解正确的话,就不需要循环:
假设范围在 12 到 76 之间:
ls -l | grep -e "file_1[2-9].txt" -e "file_[2-6][0-9].txt" -e "file_7[0-6].txt"
文件 12 至 19
"file_1[2-9].txt"
文件 20 至 69:
"file_[2-6][0-9].txt"
文件 70 至 76
"file_7[0-6].txt"