在子命令中执行时,file命令无法打开文件

时间:2017-08-09 15:13:37

标签: bash unix

为什么文件命令在子命令中运行时无法打开文件,而basename之类的命令工作得很好?

ls
file1  file2  file3

find * -exec echo $(basename {}) \;
file1
file2
file3

find * -exec echo $(file {}) \;
file1: cannot open `file1' (No such file or directory)
file2: cannot open `file2' (No such file or directory)
file3: cannot open `file3' (No such file or directory)

find * -exec file {} \;
file1: empty
file2: empty
file3: empty

1 个答案:

答案 0 :(得分:2)

为什么原始代码会中断

因为子命令在 find之前运行,所以这是完全正常和预期的行为。那就是:

find * -exec echo $(basename {}) \;

......先跑......

basename {}

...返回字符串{},发出命令:

find * -exec echo {} \;

...根本不会为三个单独的文件中的每一个运行basename

同样,find * -exec echo $(file {}) \;首先运行file {},并且由于这会在stdout 上返回错误消息,请将该错误替换为稍后由find运行的命令。

做什么

如果你想在find启动的shell中运行某些东西,你需要告诉find启动那个shell:

# note that {} is a separate argument; this is important for security reasons.
find . -exec sh -c 'echo $(basename "$1")' _ {} \;

......顺便说一下,这是一个非常非常愚蠢且不必要的低效写作方式:

find . -exec basename {} \;

顺便说一下,如果你需要一个shell,你可以通过传递一个shell多个参数来提高效率:

find . -exec 'for arg; do basename "$arg"; done' _ {} +

...或者您可以直接在父进程中执行循环,而不是完全使用find -exec

# note that this requires a bash, not /bin/sh, shebang.
while IFS= read -r -d '' filename <&3; do
  basename "$filename"
done 3< <(find . -print0)

要做什么

以下看起来就像它有效:

# THIS IS EVIL; DO NOT DO THIS
find . -exec sh -c 'echo $(basename "{}")' \;

...但是,它实际上会在您的系统中引入严重的安全漏洞。为什么?假设有人制作了这样的文件:

touch $'$(touch i-am-evil)\'$(touch i-am-evil)\''

...当你的find命令发生时,它会运行:

echo $(basename "$(touch i-am-evil)'$(touch i-am-evil)'")

...并且touch i-am-evil开始运行。 (将其替换为攻击者可能希望在运行find的用户的权限的情况下在您的系统上执行的任意命令。)