Php 将路径的basename注入find exec命令

Php 将路径的basename注入find exec命令,php,bash,silverstripe-4,Php,Bash,Silverstripe 4,我有一个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=$(base

我有一个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)变量的大写字母)

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

我并不是为了寻找而结婚的,我很乐意使用其他任何东西,但希望我能把它保持在一行


非常感谢你

这主要是一个shell引用问题,与
find
本身关系不大

在Unix shell(如bash)中,变量扩展(如
${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
(注意:您不应使用所有caps外壳变量名称。所有caps中的变量名称,如
$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
的值已拆分为两个字,然后由于第二个字是文件模式,因此将其扩展为两个文件名。因此,
echo
被调用时有三个参数--
go
a
b
--它用一个空格分隔它们

当命令行实用程序是shell解释器时,这会影响所使用的变量是属于外壳(并在命令行传递到内部外壳之前展开),还是作为文本字符串传递到内部外壳,然后在执行时将其展开:

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

$ bash -c 'dir=inner; echo $dir'
inner
这两个公式都不是真正正确的,因为内部shell看到了一个不带引号的参数扩展,这可能导致不必要的分词和文件名扩展。通常,我们将使用的版本为:

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
的值,然后删除与模式匹配的最长前缀
*/
。(单个
#
可能是最短的匹配。)最长的前缀匹配
*/
是整个目录路径,因为它延伸到最后一个
/
;删除后剩下的就是basename

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' _ {} \;

多么令人惊讶的答案。。。非常感谢你!