到目前为止我有这个:
sudo find /path/to/dir -type f |
xargs -d "\n" sudo stat -c "%Y %n" |
{arithmetic to check if %Y is between 1685518962 and 1685624474??} |
{show file path}
需要注意的是,我知道find -newermt
但更普遍地询问有关在命令行中执行算术的问题。
澄清一下,我希望能有一句台词。
TCSH 或 BASH 对我来说是可以接受的,但请不要犹豫,用另一种 shell 来启发我。
答案1
使用 NUL 分隔的记录(-print0
for find
、-0
for xargs
)来处理文件列表,而不是行,因为换行符与文件路径中的任何其他字符(或非字符)一样有效。
由于您已经在使用 GNU 扩展,因此您可以执行以下操作:
sudo find /path/to/dir -type f '(' \
-newermt @1685518962 ! -newermt @1685624474 -exec something with {} + \
-o -exec something-else with {} + ')'
早在 GNU命令被引入之前, GNUfind
就已经能够从文件中报告元数据(具有更好的界面) 。stat
对于后处理,您可以使用任何支持 NUL 分隔记录和浮点运算的工具,例如 gawk/mawk、perl 或 zsh:
sudo find -H /path/to/dir -type f -printf '%T@\0%p\0' |
LC_ALL=C gawk -v RS='\0' -F: '
{mtime = $0; getline file}
mtime > 1685518962 && mtime <= 1685624474 {
# something with mtime and file
}'
和perl
:
sudo find -H /path/to/dir -type f -printf '%T@\0%p\0' | perl -0lne '
$mtime = $_; $file = <>;
if ($mtime > 1685518962 && $mtime <= 1685624474) {
# something with $mtime and $file
}'
包括此处给出的任何示例的任何代码都可以放在一行中。如果是高尔夫练习,perl
可能是其中最适合的:
sudo find -H /path/to/dir -type f -printf '%p\0%T@\0'|perl -0lne'$m=<>;{...}if$m>1685518962&&$m<=1685624474'
或者如果使用zsh
,以防您需要 shell 来参与这些文件的处理:
sudo find -H /path/to/dir -type f -printf '%T@/%p\0' |
while IFS=/ read -rd '' mtime file; do
(( mtime > 1685518962 && mtime <= 1685624474 )) &&
something with $mtime and $file
done
虽然zsh
还具有内置的大部分find
功能,以及一个内置的stat
(早于 GNU 的stat
),这将避免依赖 GNUisms,但可能会更慢:
sudo zsh -c '
zmodload zsh/stat
for file ( /path/to/dir/**/*(ND.) ) {
stat -LF %s.%N -A mtime +mtime -- $file &&
(( mtime > 1685518962 && mtime <= 1685624474 )) &&
something with $file and $mtime
}'
在当今这个时代,我不会使用 tcsh,因为几乎不可能用它可靠地做任何事情,但既然你提到了它,请注意它确实支持递归通配符(在 2010 年 bash 之后不久从 zsh 复制而来),并且具有内置功能支持检索文件的 mtime,但不具有亚秒精度(无论如何其算术仅是整数)并且仅在符号链接解析之后。
set globstar globdot
foreach file ( /path/to/dir/** )
if ( -f $file:q && ! -l $file:q ) then
@ mtime = -M $file:q
if ( $mtime > 1685518962 && $mtime <= 1685624474 ) then
something with $file:q and $mtime
endif
endif
end
请注意,虽然其他的获取的 mtime 具有完整的纳秒精度,但大多数计算机/C 编译器的浮点数类型以及//的double
结果是无法保持当今时间戳所需的精度。awk
perl
zsh
例如,其中大多数都无法报告上次修改的文件位于1685518962.000000001
。对于你的情况来说可能不是什么大问题;例如,如果您需要高精度地查找给定微秒内最后修改的文件,情况可能会如此。
另请注意:因为将环境变量sudo
传递SUDO_COMMAND
给它执行的命令,并且该变量包含所有参数,这意味着xargs sudo cmd
可能会失败“争论太多”如果存在大量文件,则会出现错误,因为execve()
参数和环境字符串的累积大小受到限制,因此虽然xargs
可以设法运行sudo
,但可能会因累积大小在其间翻倍而sudo
无法运行。cmd
出于这个原因,也因为它可以节省运行一些sudo
调用的时间,所以最好使用sudo xargs cmd
.在这里你也可以sudo
只调用一次sudo sh -c '...'
¹ 可以通过-MvPREC=100
最近版本中的选项来解决,gawk
如果在构建时启用了任意精度算术,或者使用-Mbignum
in perl
,或者更一般地通过分别比较秒和纳秒(如-newertmt
或 shells/[
的-nt
运算符可能会这样做)或使用数字字符串比较的数字,而不是将其转换为固定大小的二进制文件,例如使用 with() [[ $1 = ${${(n)@}[1]} ]] $t1 $t2
而不是(( t1 < t2 ))
使用数字排序参数扩展标志zsh
。n
答案2
这听起来像是一份工作awk
:
sudo find /path/to/dir -type f |
xargs -d "\n" sudo stat -c "%Y %n" |
awk '$1 > 1685518962 && $1 < 1685624474 {print}'
您可以xargs
使用-execdir
以下选项删除find
:
sudo find /path/to/dir -type f -execdir stat -c "%Y %n" {} + |
awk '$1 > 1685518962 && $1 < 1685624474 {print}'
答案3
嗯,bash 是可以接受的
#!/bin/bash
shopt -s dotglob globstar extglob nullglob
oldest=1685518962
newest=1685624474
for file in **/*; do
# check for regular file-ness
modtime=$(stat -c '%Y' -- "${file}")
[[ ( ! -L ${file} ) \
&& -f ${file} \
&& ( ${modtime} -gt ${oldest} ) \
&& ( ${modtime} -le ${newest} ) ]] \
&& {
# do stuff
}
done
Zsh 在 globbing 可以轻松完成的事情上要漂亮一些
#!/usr/bin/zsh
zmodload zsh/stat
oldest=1685518962
newest=1685624474
for file in **/*(.ND); do
modtime=$(zstat -L +mtime -- "${file}")
[[ ${modtime} -gt ${oldest} \
&& ${modtime} -le ${newest} ]] \
&& {
# do stuff
}
done
因为你的问题字面上地要求算术运算,我会指出 zsh 和 bash 都支持扩展中的算术运算:$(( ${modtime} - ${oldest} ))
将为您提供自下限秒以来的秒数。 Zsh 的算术能力要好一些,特别是在浮点方面。
答案4
尽管有其他答案的建议,一般来说,您需要组合两个命令来创建一个可在管道中使用的单行 shell 算术过滤器函数:
xargs
能够映射输入行,sh
访问 shell 算术函数
在图案中xargs -L1 sh -c '_() { ... }; _ "$@"'
对于您的具体示例,这看起来像:
find ...
| xargs stat ...
| xargs -L1 sh -c '_() { [ 1685518962 -le $0 -a $0 -le 1685624474 ] && echo $@; }; _ "$@"'
这适用于快速而肮脏的任务,就像您这里的任务一样。但是,如果您有任何数据量,您确实希望部署一个内置 Map-Reduce 功能的工具,例如awk
.