我尝试为作业编写基本find
命令(不使用find
)。现在我有一些我想要exec
的文件。语法如下所示:
-exec /bin/mv {} ~/.TRASH
我有一个名为current
的数组,它包含所有文件。我的数组只保存/bin/mv
,{}
和~/.TRASH
(因为我将-exec
移出)并且位于名为arguments
的数组中。
我需要它,以便每个文件都传递到{}
并在其上调用exec。
我认为我应该使用sed
替换{}
这样的内容(在for循环中):
for i in "${current[@]}"; do
sed "s@$i@{}"
#exec stuff?
done
我如何执行其他参数?
答案 0 :(得分:1)
你可以这样:
cmd='-exec /bin/mv {} ~/.TRASH'
current=(test1.txt test2.txt)
for f in "${current[@]}"; do
eval $(sed "s/{}/$f/;s/-exec //" <<< "$cmd")
done
请谨慎使用eval
命令,因为如果输入来自不受信任的来源,它可能会产生令人讨厌的事情。
这是试图避免eval
(感谢@gniourf_gniourf的评论):
current=( test1.txt test2.txt )
arguments=( "/bin/mv" "{}" ~/.TRASH )
for f in "${current[@]}"; do
"${arguments[@]/\{\}/$f}"
done
答案 1 :(得分:1)
你很幸运,你的设计并不太糟糕,你的论点都在一个阵列中。
但你肯定不想使用eval
。
所以,如果我理解正确,你有一个文件数组:
current=( [0]='/path/to/file'1 [1]='/path/to/file2' ... )
和一系列参数:
arguments=( [0]='/bin/mv' [1]='{}' [2]='/home/alex/.TRASH' )
请注意,这里没有波浪号,因为Bash已经扩展了它。
执行您想要的任务:
for i in "${current[@]}"; do
( "${arguments[@]//'{}'/"$i"}" )
done
观察报价。
这将通过{}
的扩展(即文件名 1 )替换arguments
字段中$i
的所有出现次数,并执行这种扩张。请注意,数组的每个字段将扩展为一个参数(由于引号),因此所有这些对于空格,glob字符等都非常安全。这是最安全和最正确的方法。使用eval
的每个解决方案都有潜在的危险和破坏(除非使用某些特殊引用,例如printf '%q'
,但这会使该方法无用尴尬)。顺便说一下,使用sed
也至少在两个方面被打破了。
请注意,我将扩展包含在子shell中,因此用户无法干扰您的脚本。如果没有这个,并且根据你的完整脚本的编写方式,很容易让你的脚本突然(恶意地)改变一些变量或者cd
- 在其他地方。在子shell中运行你的参数,或者在一个单独的进程中运行你的参数(例如,单独的bash或sh实例 - 但这会增加额外的开销)是出于明显的安全原因而非常强制的!
请注意,使用您的脚本,用户可以直接访问所有Bash内置版本(这是一个巨大的专业版),与一些更标准的find
版本 2 相比!
1 请注意POSIX clearly specifies that this behavior is implementation-defined:
如果 utility_name 或参数字符串包含两个字符&#34; {}&#34;,而不仅仅是两个字符&#34; {} &#34;,是实现定义的 find 是否替换这两个字符或使用字符串而不做更改。
在我们的例子中,我们选择用文件名替换所有出现的{}
。这与例如GNU find
的行为相同。来自man find
:
字符串
{}
被在命令参数中出现的任何位置处理的当前文件名替换,而不仅仅是在单独的参数中,就像在某些版本的find中一样。
2 POSIX also specifies that calling builtins is not defined:
如果 utility_name 命名任何特殊的内置实用程序(请参阅Special Built-In Utilities),结果将是未定义的。
在你的情况下,它定义得很好!
我认为尝试实现(在纯粹的Bash中)一个find
命令是一个很好的练习,应该教你很多...特别是如果你得到相关的反馈。我很乐意审核您的代码!