免责声明:我是 Unix/Linux 的新手,但我期待学习!我已经尝试在这个 stackexchange 上搜索并阅读 the man find
,但我似乎无法弄清楚这一点。
我想使用该find ... -exec {} +
命令递归地查找具有特定文件扩展名的所有文件,并在文件列表上运行命令。我需要转换大约 100k 个文件。我正在运行的命令接受文件名(或文件名列表,例如f1 f2 f3
)作为参数,但我还需要指定其他参数来运行该命令。
到目前为止我尝试过的:
这有效:
find . -iname "*.extension" -exec <command> {} <additional parameters> \;
这似乎不起作用:
find . -iname "*.extension" -exec <command> {} <additional parameters> +
我收到错误消息,find: missing argument to '-exec'
。我猜我无法在{}
?之后指定其他参数
一些注意事项:
该命令将文件名作为第一个参数,然后我需要指定一些附加参数,例如输出目录-o <outputDir>
和从文件中提取的变量-v <var1,var2,...>
。
我正在 Ubuntu 12.04 的终端上运行它,如果这有什么区别的话。
答案1
find . -iname "*.extension" -exec sh -c '
exec <command> "$@" <additional parameters>' sh {} +
答案2
有了 ,+
它将列出由空格分隔的多个文件名{}
(这将是一个很长的列表,因为您有 100000 个文件),而不仅仅是一个文件名。既然如此,则{}
需要在命令末尾出现。
请参阅find(1)
下面的手册页-exec command {} +
。
答案3
假设所有目录和文件都有常规名称,即不包含空格、换行符或类似名称,即使文件数量巨大,这也应该有效:
find . -iname "*.extension" -exec sh -c '
command="<command>"
additionalParameters="<additional parameters>"
h=$(($#/2))
cmd="$command "
for i in $(seq 1 $h);do
cmd="$cmd $(eval echo \$$i) "
done
cmd="$cmd $additionalParameters"
$cmd
shift $h
$command "$@" $additionalParameters' sh {} +
理由:
使用+
标点符号时,find 会构建尽可能大的命令。涉及两个限制:允许的最大参数数量(在 Gnu/Linux 上应为 128k)和参数列表的最大大小(在 Gnu/Linux 上应为 2 MB)。问题是调用的命令需要额外的参数(附加参数)。添加它们会溢出限制,导致“参数过多错误”。我建议的脚本将构建的参数列表分成两部分,并运行两个命令,而不是每个块运行一个命令,因此添加额外的参数不会出现问题。
答案4
您可以使用这个脚本:
#! /bin/bash
cmd=echo
test $# -gt 2 || exit 2
num_trailing_args="$1"
[[ $num_trailing_args =~ ^(0|[1-9][0-9]*)$ ]] ||
{ echo "Illegal first argument ('${num_trailing_args}'); aborting"; exit 2; }
test $# -lt $((num_trailing_args+2)) &&
{ echo "Too few arguments; aborting"; exit 2; }
shift
trailing_args=()
for((i=0;i<num_trailing_args;i++)); do
trailing_args[i]="$1"
shift
done
"$cmd" "$@" "${trailing_args[@]}"
然后使用
find ... -exec args_change_script.sh 3 t1 t2 t3 {} +
命令的名称不应长于脚本的名称(只是为了确定)。