bash - 传递变量以查找命令-exec

时间:2016-06-25 01:31:20

标签: bash sed

我有一个像这样的脚本:

#!/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: `"'

即使我echo $SED_COMMAND,它显示正确的命令。什么错了

2 个答案:

答案 0 :(得分:3)

数组将起作用:

#!/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[@]}" {} +

说明:

  • 扩展字符串不加引号(如在失败的尝试中)不通过完整的解析器运行扩展结果,而是仅通过字符串拆分和glob-expansion阶段;因此,报价不会被解析和消费。这是一个功能,而不是一个错误 - 如果每个扩展运行完整的解析器,在shell中编写安全代码实际上是不可能的。请阅读BashFAQ #50以获取有关手头行为和最佳做法解决方法的完整说明。

    提供更简单,更简单的例子:

    a_command='touch "hello world"'
    $a_command
    

    ...实际上会创建两个文件:一个名为"hello,另一个名为world";这是因为经过的唯一处理步骤$a_command是字符串拆分(基于空格拆分为单词)和glob扩展(评估该拆分过程创建的每个项目以查看它是否为glob表达式,并将其替换为匹配文件列表(如果是这样)。

  • -exec ... {} +将多个输入文件名传递给exec'd命令的每次调用;因此,在允许的情况下提高效率(这肯定是这种情况,因为支持sed扩展名的任何-i也支持多个输入文件。)

答案 1 :(得分:1)

我想最简单的方法就是在这里使用一个函数。

#!/usr/bin/env bash=
#Below function wraps the whole of sed processing part
sed_fun()
{
OLD_PATH_BASE=./components/
NEW_PATH_BASE=../../../react_components/
sed -i "s@^\(import \w\+ from '\)$OLD_PATH_BASE\(\w\+/\w\+\.jsx';\)@\1$NEW_PATH_BASE\2@" "$@"
}

export -f sed_fun

find . -regextype sed -regex "\./rockstart_\w\+_app/static/js/\w\+\.js" -exec bash -c 'sed_fun "$0"' {} \;

或使用数组:

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 bash -c 'sed_fun  "$@"' _ {} \+

注意:作为评论中提及的@Charles Duffy,对应用程序使用完全大写变量不是一个好习惯,因为这些变量通常是为系统保留的。

<强>参考

  1. find [ Complex Actions ],了解-exec bash -c 'sed_fun "$0"' {} \;

  2. 的详细说明
  3. 查找[ manpage ]