使用 Find 列出文件并在输出中包含计数器值

使用 Find 列出文件并在输出中包含计数器值

出于性能原因,我想用来find列出大量文件,但也在每行上包含一个计数器。以下是我到目前为止所拥有的:

local root_dir="."
local ouptut_file="/tmp/foo.txt"

File_Number=99              ## Initial value

echo "" > "${ouptut_file}"  ## Initialize file to empty

find "$root_dir"   \
    -type f        \
    -depth 2       \
    -name '*.xxx'  \
    -print0        \
| sort -z          \
| xargs -0  printf "DoSomething %5d '%s'\n" $[File_Number++] -- \
> "${ouptut_file}"

这输出

DoSomething    99 '--'
DoSomething     0 'dirA/dirB/file1.xxx'
DoSomething     0 'dirA/dirB/file2.xxx'
DoSomething     0 'dirA/dirB/file3.xxx'

我想要的是

DoSomething     100 'dirA/dirB/file1.xxx'
DoSomething     101 'dirA/dirB/file2.xxx'
DoSomething     102 'dirA/dirB/file3.xxx'

我如何包含计数器并消除输出中的第一个虚假线。

系统bash:正在 macOS Ventura 13.0.1 上使用

答案1

一些注意事项:

  1. $[...]是一种非常古老的算术展开式。如今 bash 文档甚至没有提及它。代替使用$((...))
  2. xargs将为该命令的每次调用启动一个新进程,因此对其中一个变量的任何更改都不会影响其他调用。
  3. 对于printf(1),--就像任何其他参数一样。发生的事情是xargs运行printf "DoSomething %5d '%s'\n" $[File_Number++] dirA/dirB/file1.xxx dirA/dirB/file2.xxx ..., 并printf循环遍历参数以满足格式字符串要求。因此,每个备用文件名实际上都被视为一个数字,从而导致0输出。

您可能想使用类似的东西:

find "$root_dir"   \
    -type f        \
    -depth 2       \
    -name '*.xxx'  \
    -print0        \
| sort -z          \
| while read -r -d '' file; do
     printf "DoSomething %5d '%s'\n" $((File_Number++)) "$file"
  done > "${ouptut_file}"

另请注意,如果您实际上使用它来打印在 shell 中运行的命令,则应该使用%q而不是'%s'正确引用。从help printf

除了标准 printf(1) 格式之外,%b 表示在相应参数中展开反斜杠转义序列,%q 表示以可重复用作 shell 输入的方式引用参数。

答案2

我会用perl

find "$root_dir"   \
    -type f        \
    -depth 2       \
    -name '*.xxx'  \
    -print0        \
| sort -z          \
| perl -l -0ne 'printf "something %05d %s\n", $., $_' > "$output_file"

如果您需要引用文件,放在'任何一边可能都不够,具体取决于文件名可能包含的内容以及这些引号的用途。例如,在包括某些 shell 在内的多种语言中,引号\和换行符在引号内仍然是特殊的,需要转义。在任何情况下,'字符都不能嵌入单引号字符串中。

如果引用是针对类似 Bourne 的 shell,则可以使用以下命令进行引用:

sub shquote {return "'" . ($_[0] =~ s/'/'\\''/gr).  "'"}

函数其中是这些 shell 中最安全的引用形式1.所以在这里:

perl -l -0ne '
  sub shquote {return "'\''" . ($_[0] =~ '"s/'/'\\\\''/gr"').  "'\''"}
  printf "something %05d %s\n", $., shquote($_)'

或者,如果您很难跟踪所有这些引言:

perl -l -0nse '
  sub shquote {return $q . ($_[0] =~ s/$q/$q\\$q$q/gr) . $q}
  printf "something %05d %s\n", $., shquote($_)' -- -q="'"

另请注意,从安全角度来看,输出到全局可写区域中具有固定名称的文件是不好的做法。例如,某人可能会创建与您具有写入权限的某些敏感文件同名的符号链接。在 /tmp 中创建文件只能使用随机生成的文件名和O_NOFOLLOW类似的mktemp操作,或者在新创建的私有目录中完成(如果无法创建,例如它已经存在,则退出)。


1String::ShellQuote在任何情况下都比 CPAN 模块更安全很少 问题

相关内容