出于性能原因,我想用来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
一些注意事项:
$[...]
是一种非常古老的算术展开式。如今 bash 文档甚至没有提及它。代替使用$((...))
。xargs
将为该命令的每次调用启动一个新进程,因此对其中一个变量的任何更改都不会影响其他调用。- 对于
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
操作,或者在新创建的私有目录中完成(如果无法创建,例如它已经存在,则退出)。