首页 > 解决方案 > 将路径的基本名称注入 find exec 命令

问题描述

我有一个有效的 PHP 命令:

php ~/.composer/vendor/bin/upgrade-code add-namespace "Vendor\Module\Finaldir" mymodule/finaldir  --write -r -vvv" \;

我想将它包装到一个find -exec调用(或类似的)中,以递归地对文件夹执行此操作。

find mymodule/src -mindepth 1 -maxdepth 2 -type d -exec sh -c "DIR=$(basename {}); php ~/.composer/vendor/bin/upgrade-code add-namespace "Vendor\\Module\\${DIR}" {}  --write -r -vvv" \;

不幸的是,上面的代码不起作用(并且也不包括 basename (DIR) 变量的大写字母)。

实现这一目标的最佳方法是什么?

我根本没有结婚找,我很乐意使用其他任何东西,但希望我能保持在一条线上。

太感谢了!

标签: phpbashsilverstripe-4

解决方案


这主要是一个shell引用问题,与此无关find

在 Unix shell(例如 bash)中,变量扩展 like${dir}在传递给命令行实用程序之前由 shell 执行,除非它们用单引号括起来echo您可以使用以下命令查看此结果:

$ dir=/home/rici
$ echo $dir     # Unquoted; the expansion is performed
/home/rici
$ echo "$dir"   # Double-quoted; the expansion is performed
/home/rici
$ echo '$dir'   # Single-quoted; the string is passed unmodified
$dir

(注意:你不应该使用全大写的 shell 变量名。全大写的变量名,比如$PATH$USER,保留给系统本身使用。你自己的变量名应该是小写的。)

普通版本和双引号版本之间的区别在于,双引号版本保留了空格和文件模式字符,而未引用版本有效地将扩展结果拆分为单独的单词,然后尝试在结果:

$ ls             # Files in this directory
a  b
$ dir='go   *'   # Note extra spaces
$ echo $dir      # The variable's value is re-split and expanded
go a b
$ echo "$dir"    # Just the value of the variable
go   *
$ echo '$dir'    # Just the characters of the argument
$dir

请注意,在未引用的版本中,后面的三个空格go已消失。的值$dir已经被拆分为两个单词,然后由于第二个单词是文件模式,所以将其扩展为两个文件名。Soecho使用三个参数 --和-- 调用go,它用一个空格分隔它们打印。ab

当命令行实用程序是 shell 解释器时,这会影响使用的变量是否属于外壳(并且在命令行传递到内壳之前被扩展),或者作为文字字符串传递到内壳,这然后在执行时扩展它:

$ dir=outer
$ bash -c "dir=inner; echo $dir"
outer

$ bash -c 'dir=inner; echo $dir'
inner

这些公式都不是真正正确的,因为内壳看到未引用的参数扩展,这可能导致不必要的分词和文件名扩展。通常,我们将使用的版本是:

bash -c 'dir=inner; echo "$dir"'

(在这种情况下,由于双引号在单引号内,因此它们也被外壳视为普通字符,因此它们被传递到内壳。)

问题之一find -exec bash -c是插入命令行的文件名按find -exec原样插入。这允许将精心设计的文件名用作注入攻击

为了避免这种情况,我们通常利用bash -c允许我们将位置参数传递给 shell 的事实。要解释的命令之后的参数bash -c分配给$0$1等。由于$0不是真正的位置参数——它应该是脚本的名称——我们通常_在命令参数之后和真正的位置参数之前放置一个虚拟参数(在下面的示例中)。所以我们最终会得到这样的结果:

find ... -exec bash -c 'echo "$1"' _ {} \;

在这里,我仔细地单引号了命令行参数,以便它按原样传递;在命令行参数中,我将参数扩展双引号,以便空格和星号不会导致任何问题,然后我find -exec将文件名作为第三个参数传递给bash -c,它将变为$1. 我希望这一切都清楚了。

所以,回到最初的问题,我们可以应用上述模式。但我要做一点改变;basename我将使用稍微神秘的 shell 语法来删除变量的前缀,而不是使用它来查找文件名的最后一个组成部分。${var##*/}取 的 值,$var然后删除与模式匹配的最长前缀*/。(单个#将是最短的匹配。)最长的前缀匹配*/是整个目录路径,因为它延伸到最后一个/; 删除后剩下的正是基本名称。

find mymodule/src -mindepth 1 -maxdepth 2 -type d \
     -exec sh -c 'dir=${1##*/}; php ~/.composer/vendor/bin/upgrade-code add-namespace "Vendor\\Module\\$dir" "$dir" --write -r -vvv' _ {} \;

推荐阅读