我有一个像这样的脚本:
#!/usr/bin/env bash
OLD_PATH_BASE=./components/
NEW_PATH_BASE=../../../react_components/
find . -regextype sed -regex "\./rockstart_\w\+_app/static/js/\w\+\.js" -exec sed -i "s@^\(import \w\+ from '\)$OLD_PATH_BASE\(\w\+/\w\+\.jsx';\)@\1$NEW_PATH_BASE\2@" {} \;
这正如我所期望的那样,但是当我这样做时:
#!/usr/bin/env bash
OLD_PATH_BASE=./components/
NEW_PATH_BASE=../../../react_components/
SED_COMMAND='sed -i "s@^\(import \w\+ from '"'\)$OLD_PATH_BASE\(\w\+/\w\+\.jsx';\)@\1$NEW_PATH_BASE\2@"'"'
find . -regextype sed -regex "\./rockstart_\w\+_app/static/js/\w\+\.js" -exec $SED_COMMAND {} \;
它不起作用并给我错误:
sed: -e expression #1, char 1: unknown command: `"'
即使当我回显$SED_COMMAND
时,它也会显示正确的命令。怎么了
最佳答案
数组可以工作:
#!/usr/bin/env bash
old_path_base=./components/
new_path_base=../../../react_components/
# this is totally unnecessary, but IMHO makes your code easier to read
# ...huge long lines being unweildy by nature, after all.
sed_pieces=(
's'
"^\(import \w\+ from '\)${old_path_base}\(\w\+/\w\+\.jsx';\)"
"\1${new_path_base}\2"
'' # flags; empty set
)
# ...using an array for sed_command is the important part:
IFS='@' # this makes "${array[*]}" combine pieces with @s
sed_command=( sed -i "${sed_pieces[*]}" )
find . -regextype sed \
-regex "\./rockstart_\w\+_app/static/js/\w\+\.js" \
-exec "${sed_command[@]}" {} +
说明:
扩展不带引号的字符串(如失败的尝试)不会通过完整的解析器运行扩展结果,而是仅通过字符串分割和全局扩展阶段;因此,引号不会被解析和消耗。这是一个功能,而不是一个错误——如果每个扩展都运行完整的解析器,那么在 shell 中编写安全代码实际上是不可能的。阅读 BashFAQ #50有关当前行为和最佳实践解决方法的完整说明。
提供一个更短、更简单的示例:
a_command='touch "hello world"' $a_command
...实际上会创建两个文件:一个名为
“hello
,另一个名为world”
;这是因为$a_command
运行的唯一处理步骤是字符串分割(根据空格分割成单词)和 glob 扩展(评估该分割过程创建的每个项目以查看它是否是 glob 表达式,并将其替换为匹配文件列表(如果是)。-exec ... {} +
将多个输入文件名传递给exec
命令的每次调用;因此,在允许的情况下,这是一种效率改进(这里肯定是这种情况,因为任何支持-i
扩展的sed
也将支持多个输入文件)。
关于bash - 传递变量来查找命令-exec,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38024121/