将路径的基本名称注入 find exec 命令
inject basename of path into find exec command
我有一个有效的 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
(注意:您不应使用全大写的 shell 变量名。全大写的变量名,如 $PATH
和 $USER
保留供系统本身。你自己的变量名应该是小写的。)
普通版本和双引号版本的区别在于双引号版本保留了白色space和文件模式字符,而未引号版本有效地将扩展结果拆分为单独的词,然后尝试在结果中扩展文件模式:
$ 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
后面的三个 space 已消失。 $dir
的值被拆分成两个单词,然后由于第二个单词是文件模式,因此将其扩展为两个文件名。所以 echo
被调用时带有三个参数——go
、a
和 b
——它用一个 space 分隔它们。
当命令行实用程序是shell解释器时,这会影响使用的变量是否属于外部shell(并且在命令行传递给内部shell),或者作为文字字符串传递给内部 shell,然后在执行时扩展它:
$ dir=outer
$ bash -c "dir=inner; echo $dir"
outer
$ bash -c 'dir=inner; echo $dir'
inner
这些公式都不是真正正确的,因为内部 shell 看到未加引号的参数扩展,这可能导致不需要的分词和文件名扩展。通常,我们使用的版本是:
bash -c 'dir=inner; echo "$dir"'
(在这种情况下,由于双引号在单引号内,它们也被外部 shell 视为普通字符,因此它们被传递给内部 shell。 )
find -exec bash -c
的一个问题是 find -exec
插入命令行的文件名是按原样插入的。这允许将精心制作的文件名用作 injection attack.
为了避免这种情况,我们通常利用 bash -c
也允许我们将位置参数传递给 shell 这一事实。要解释的命令后 bash -c
的参数分配给 [=31=]
、</code>,依此类推。由于 <code>[=31=]
不是真正的位置参数——它应该是脚本的名称——我们通常在命令参数之后和真正的参数之前放置一个虚拟参数(下例中的 _
)位置参数。所以我们最终会得到这样的结果:
find ... -exec bash -c 'echo ""' _ {} \;
在这里,我小心地将命令行参数用单引号引起来,以便按原样传递;在命令行参数中,我双引号扩展参数,这样 spaces 和 stars 不会造成任何问题,然后我得到 find -exec
将文件名作为第三个参数传递给 bash -c
,它会变成 </code>。我希望一切都清楚。</p>
<p>所以,对于return原题,我们可以套用上面的模式。但我要做一点改变;我不使用 <code>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' _ {} \;
我有一个有效的 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
(注意:您不应使用全大写的 shell 变量名。全大写的变量名,如 $PATH
和 $USER
保留供系统本身。你自己的变量名应该是小写的。)
普通版本和双引号版本的区别在于双引号版本保留了白色space和文件模式字符,而未引号版本有效地将扩展结果拆分为单独的词,然后尝试在结果中扩展文件模式:
$ 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
后面的三个 space 已消失。 $dir
的值被拆分成两个单词,然后由于第二个单词是文件模式,因此将其扩展为两个文件名。所以 echo
被调用时带有三个参数——go
、a
和 b
——它用一个 space 分隔它们。
当命令行实用程序是shell解释器时,这会影响使用的变量是否属于外部shell(并且在命令行传递给内部shell),或者作为文字字符串传递给内部 shell,然后在执行时扩展它:
$ dir=outer
$ bash -c "dir=inner; echo $dir"
outer
$ bash -c 'dir=inner; echo $dir'
inner
这些公式都不是真正正确的,因为内部 shell 看到未加引号的参数扩展,这可能导致不需要的分词和文件名扩展。通常,我们使用的版本是:
bash -c 'dir=inner; echo "$dir"'
(在这种情况下,由于双引号在单引号内,它们也被外部 shell 视为普通字符,因此它们被传递给内部 shell。 )
find -exec bash -c
的一个问题是 find -exec
插入命令行的文件名是按原样插入的。这允许将精心制作的文件名用作 injection attack.
为了避免这种情况,我们通常利用 bash -c
也允许我们将位置参数传递给 shell 这一事实。要解释的命令后 bash -c
的参数分配给 [=31=]
、</code>,依此类推。由于 <code>[=31=]
不是真正的位置参数——它应该是脚本的名称——我们通常在命令参数之后和真正的参数之前放置一个虚拟参数(下例中的 _
)位置参数。所以我们最终会得到这样的结果:
find ... -exec bash -c 'echo ""' _ {} \;
在这里,我小心地将命令行参数用单引号引起来,以便按原样传递;在命令行参数中,我双引号扩展参数,这样 spaces 和 stars 不会造成任何问题,然后我得到 find -exec
将文件名作为第三个参数传递给 bash -c
,它会变成 </code>。我希望一切都清楚。</p>
<p>所以,对于return原题,我们可以套用上面的模式。但我要做一点改变;我不使用 <code>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' _ {} \;