I'm trying to write a basic find
command for a assignment (without using find
). Right now I have an array of files I want to exec
something on. The syntax would look like this:
我正在为一个作业编写一个基本的查找命令(不使用find)。现在我有一个文件数组,我想执行一些东西。语法应该是这样的:
-exec /bin/mv {} ~/.TRASH
- exec /bin/mv { } ~ / .TRASH
And I have an array called current
that holds all of the files. My array only holds /bin/mv
, {}
, and ~/.TRASH
(since I shift the -exec
out) and are in an array called arguments
.
我有一个名为current的数组,它包含所有的文件。我的数组只包含/bin/mv、{}和~/。dump(因为我将-exec移出)并位于一个名为arguments的数组中。
I need it so that every file gets passed into {}
and exec is called on it.
我需要它,以便每个文件都传递到{},并对其调用exec。
I'm thinking I should use sed
to replace the contents of {}
like this (within a for loop):
我在想我应该使用sed来替换像这样的{}的内容(在for循环中):
for i in "${current[@]}"; do
sed "s@$i@{}"
#exec stuff?
done
How do I exec the other arguments though?
我该如何解释其他的论点呢?
2 个解决方案
#1
1
You can something like this:
你可以这样:
cmd='-exec /bin/mv {} ~/.TRASH'
current=(test1.txt test2.txt)
for f in "${current[@]}"; do
eval $(sed "s/{}/$f/;s/-exec //" <<< "$cmd")
done
Be very careful with eval
command though as it can do nasty things if input comes from untrusted sources.
不过要非常小心使用eval命令,因为如果输入来自不可信的源,它可能会做一些令人讨厌的事情。
Here is an attempt to avoid eval
(thanks to @gniourf_gniourf for his comments):
这里是一个避免eval的尝试(感谢@gniourf_gniourf的评论):
current=( test1.txt test2.txt )
arguments=( "/bin/mv" "{}" ~/.TRASH )
for f in "${current[@]}"; do
"${arguments[@]/\{\}/$f}"
done
#2
1
Your are lucky that your design is not too bad, that your arguments are in an array.
幸运的是,您的设计并不是很糟糕,您的参数在一个数组中。
But you certainly don't want to use eval
.
但是你肯定不想用eval。
So, if I understand correctly, you have an array of files:
所以,如果我理解正确,你有一个文件数组:
current=( [0]='/path/to/file'1 [1]='/path/to/file2' ... )
and an array of arguments:
以及一系列参数:
arguments=( [0]='/bin/mv' [1]='{}' [2]='/home/alex/.TRASH' )
Note that you don't have the tilde here, since Bash already expanded it.
注意,这里没有倾斜,因为Bash已经扩展了它。
To perform what you want:
去做你想做的:
for i in "${current[@]}"; do
( "${arguments[@]//'{}'/"$i"}" )
done
Observe the quotes.
观察引号。
This will replace all the occurrences of {}
in the fields of arguments
by the expansion of $i
, i.e., by the filename1, and execute this expansion. Note that each field of the array will be expanded to one argument (thanks to the quotes), so that all this is really safe regarding spaces, glob characters, etc. This is really the safest and most correct way to proceed. Every solution using eval
is potentially dangerous and broken (unless some special quotings is used, e.g., with printf '%q'
, but this would make the method uselessly awkward). By the way, using sed
is also broken in at least two ways.
这将通过扩展$i来替换参数字段中出现的所有{},即。,通过filename1,并执行此扩展。注意,数组的每个字段都将扩展为一个参数(多亏了引号),因此对于空格、通配符等等,所有这些都是安全的。使用eval的每个解决方案都有潜在的危险和破坏(除非使用一些特殊的引用,例如使用printf '%q',但是这会使方法变得毫无用处地笨拙)。顺便说一下,使用sed至少有两种方法。
Note that I enclosed the expansion in a subshell, so that it's impossible for the user to interfere with your script. Without this, and depending on how your full script is written, it's very easy to make your script break by (maliciously) changing some variables stuff or cd
-ing somewhere else. Running your argument in a subshell, or in a separate process (e.g., separate instance of bash or sh—but this would add extra overhead) is really mandatory for obvious security reasons!
注意,我将扩展包含在子shell中,因此用户不可能干扰您的脚本。如果没有这些,并且取决于您的完整脚本是如何编写的,那么很容易使您的脚本(恶意地)通过更改某些变量或在其他地方使用cd来中断。出于明显的安全原因,在子shell或单独的进程(例如,bash或sh的单独实例,但是这会增加额外的开销)中运行参数是必需的!
Note that with your script, user has a direct access to all the Bash builtins (this is a huge pro), compared to some more standard find
versions2!
注意,与一些更标准的find versions2相比,使用您的脚本,用户可以直接访问所有Bash构建程序(这是一个巨大的专业程序)!
1 Note that POSIX clearly specifies that this behavior is implementation-defined:
注意,POSIX明确指定此行为是实现定义的:
If a utility_name or argument string contains the two characters "{}", but not just the two characters "{}", it is implementation-defined whether find replaces those two characters or uses the string without change.
如果一个效用_name或参数字符串包含两个字符“{}”,但不只是两个字符“{}”,那么find是否替换这两个字符或使用字符串而不进行更改都是实现定义的。
In our case, we chose to replace all occurrences of {}
with the filename. This is the same behavior as, e.g., GNU find
. From man find
:
在本例中,我们选择用文件名替换所有出现的{}。这与GNU find的行为相同。从男人找到:
The string
{}
is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find.字符串{}被当前的文件名称所替代,它发生在命令的参数中,而不仅仅是在一个单独的参数中,就像在某些版本的find中一样。
2POSIX also specifies that calling builtins is not defined:
POSIX还指定不定义调用内置项:
If the utility_name names any of the special built-in utilities (see Special Built-In Utilities), the results are undefined.
如果效用_name命名任何特殊的内置实用程序(请参阅特殊的内置实用程序),则结果是未定义的。
In your case, it's well defined!
在你的情况下,它是很明确的!
I think that trying to implement (in pure Bash) a find
command is a wonderful exercise that should teach you a lot… especially if you get relevant feedback. I'd be happy to review your code!
我认为(在纯粹的Bash中)尝试实现find命令是一个很好的练习,应该会教会您很多……尤其是如果您得到相关的反馈。我很乐意回顾你的代码!
#1
1
You can something like this:
你可以这样:
cmd='-exec /bin/mv {} ~/.TRASH'
current=(test1.txt test2.txt)
for f in "${current[@]}"; do
eval $(sed "s/{}/$f/;s/-exec //" <<< "$cmd")
done
Be very careful with eval
command though as it can do nasty things if input comes from untrusted sources.
不过要非常小心使用eval命令,因为如果输入来自不可信的源,它可能会做一些令人讨厌的事情。
Here is an attempt to avoid eval
(thanks to @gniourf_gniourf for his comments):
这里是一个避免eval的尝试(感谢@gniourf_gniourf的评论):
current=( test1.txt test2.txt )
arguments=( "/bin/mv" "{}" ~/.TRASH )
for f in "${current[@]}"; do
"${arguments[@]/\{\}/$f}"
done
#2
1
Your are lucky that your design is not too bad, that your arguments are in an array.
幸运的是,您的设计并不是很糟糕,您的参数在一个数组中。
But you certainly don't want to use eval
.
但是你肯定不想用eval。
So, if I understand correctly, you have an array of files:
所以,如果我理解正确,你有一个文件数组:
current=( [0]='/path/to/file'1 [1]='/path/to/file2' ... )
and an array of arguments:
以及一系列参数:
arguments=( [0]='/bin/mv' [1]='{}' [2]='/home/alex/.TRASH' )
Note that you don't have the tilde here, since Bash already expanded it.
注意,这里没有倾斜,因为Bash已经扩展了它。
To perform what you want:
去做你想做的:
for i in "${current[@]}"; do
( "${arguments[@]//'{}'/"$i"}" )
done
Observe the quotes.
观察引号。
This will replace all the occurrences of {}
in the fields of arguments
by the expansion of $i
, i.e., by the filename1, and execute this expansion. Note that each field of the array will be expanded to one argument (thanks to the quotes), so that all this is really safe regarding spaces, glob characters, etc. This is really the safest and most correct way to proceed. Every solution using eval
is potentially dangerous and broken (unless some special quotings is used, e.g., with printf '%q'
, but this would make the method uselessly awkward). By the way, using sed
is also broken in at least two ways.
这将通过扩展$i来替换参数字段中出现的所有{},即。,通过filename1,并执行此扩展。注意,数组的每个字段都将扩展为一个参数(多亏了引号),因此对于空格、通配符等等,所有这些都是安全的。使用eval的每个解决方案都有潜在的危险和破坏(除非使用一些特殊的引用,例如使用printf '%q',但是这会使方法变得毫无用处地笨拙)。顺便说一下,使用sed至少有两种方法。
Note that I enclosed the expansion in a subshell, so that it's impossible for the user to interfere with your script. Without this, and depending on how your full script is written, it's very easy to make your script break by (maliciously) changing some variables stuff or cd
-ing somewhere else. Running your argument in a subshell, or in a separate process (e.g., separate instance of bash or sh—but this would add extra overhead) is really mandatory for obvious security reasons!
注意,我将扩展包含在子shell中,因此用户不可能干扰您的脚本。如果没有这些,并且取决于您的完整脚本是如何编写的,那么很容易使您的脚本(恶意地)通过更改某些变量或在其他地方使用cd来中断。出于明显的安全原因,在子shell或单独的进程(例如,bash或sh的单独实例,但是这会增加额外的开销)中运行参数是必需的!
Note that with your script, user has a direct access to all the Bash builtins (this is a huge pro), compared to some more standard find
versions2!
注意,与一些更标准的find versions2相比,使用您的脚本,用户可以直接访问所有Bash构建程序(这是一个巨大的专业程序)!
1 Note that POSIX clearly specifies that this behavior is implementation-defined:
注意,POSIX明确指定此行为是实现定义的:
If a utility_name or argument string contains the two characters "{}", but not just the two characters "{}", it is implementation-defined whether find replaces those two characters or uses the string without change.
如果一个效用_name或参数字符串包含两个字符“{}”,但不只是两个字符“{}”,那么find是否替换这两个字符或使用字符串而不进行更改都是实现定义的。
In our case, we chose to replace all occurrences of {}
with the filename. This is the same behavior as, e.g., GNU find
. From man find
:
在本例中,我们选择用文件名替换所有出现的{}。这与GNU find的行为相同。从男人找到:
The string
{}
is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find.字符串{}被当前的文件名称所替代,它发生在命令的参数中,而不仅仅是在一个单独的参数中,就像在某些版本的find中一样。
2POSIX also specifies that calling builtins is not defined:
POSIX还指定不定义调用内置项:
If the utility_name names any of the special built-in utilities (see Special Built-In Utilities), the results are undefined.
如果效用_name命名任何特殊的内置实用程序(请参阅特殊的内置实用程序),则结果是未定义的。
In your case, it's well defined!
在你的情况下,它是很明确的!
I think that trying to implement (in pure Bash) a find
command is a wonderful exercise that should teach you a lot… especially if you get relevant feedback. I'd be happy to review your code!
我认为(在纯粹的Bash中)尝试实现find命令是一个很好的练习,应该会教会您很多……尤其是如果您得到相关的反馈。我很乐意回顾你的代码!