将算术应用到管道命令中

将算术应用到管道命令中

到目前为止我有这个:

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 分隔的记录(-print0for find-0for 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结果是无法保持当今时间戳所需的精度。awkperlzsh

例如,其中大多数都无法报告上次修改的文件位于1685518962.000000001。对于你的情况来说可能不是什么大问题;例如,如果您需要高精度地查找给定微秒内最后修改的文件,情况可能会如此。

另请注意:因为将环境变量sudo传递SUDO_COMMAND给它执行的命令,并且该变量包含所有参数,这意味着xargs sudo cmd可能会失败“争论太多”如果存在大量文件,则会出现错误,因为execve()参数和环境字符串的累积大小受到限制,因此虽然xargs可以设法运行sudo,但可能会因累积大小在其间翻倍而sudo无法运行。cmd

出于这个原因,也因为它可以节省运行一些sudo调用的时间,所以最好使用sudo xargs cmd.在这里你也可以sudo只调用一次sudo sh -c '...'


¹ 可以通过-MvPREC=100最近版本中的选项来解决,gawk如果在构建时启用了任意精度算术,或者使用-Mbignumin perl,或者更一般地通过分别比较秒和纳秒(如-newertmt或 shells/[-nt运算符可能会这样做)或使用数字字符串比较的数字,而不是将其转换为固定大小的二进制文件,例如使用 with() [[ $1 = ${${(n)@}[1]} ]] $t1 $t2而不是(( t1 < t2 ))使用数字排序参数扩展标志zshn

答案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 算术过滤器函数:

  1. xargs能够映射输入行,
  2. sh访问 shell 算术函数

在图案中xargs -L1 sh -c '_() { ... }; _ "$@"'

对于您的具体示例,这看起来像:

find ...
  | xargs stat ...
  | xargs -L1 sh -c '_() { [ 1685518962 -le $0 -a $0 -le 1685624474 ] && echo $@; }; _ "$@"'

这适用于快速而肮脏的任务,就像您这里的任务一样。但是,如果您有任何数据量,您确实希望部署一个内置 Map-Reduce 功能的工具,例如awk.

相关内容