这是我的test.env
RABBITMQ_HOST=127.0.0.1
RABBITMQ_PASS=1234
我想用
test.sh
替换test.env
中的值:RABBITMQ_HOST=rabbitmq1
RABBITMQ_PASS=12345
这是我的测验
#!/bin/bash
echo "hello world"
RABBITMQ_HOST=rabbitmq1
RABBITMQ_PASS=12345
Deploy_path="./config/test.env"
sed -i 's/RABBITMQ_HOST=.*/RABBITMQ_HOST='$RABBITMQ_HOST'/' $Deploy_path
sed -i 's/RABBITMQ_PASS=.*/RABBITMQ_PASS='$RABBITMQ_HOST'/' $Deploy_path
但我有错误
sed: 1: "./config/test.env": invalid command code .
sed: 1: "./config/test.env": invalid command code .
我怎样才能修好它?
最佳答案
tl;博士:
使用BSD Sed,例如在macOS上也可以找到,您必须使用-i ''
而不仅仅是-i
(用于不创建备份文件)来使您的命令工作;例如:
sed -i '' 's/RABBITMQ_HOST=.*/RABBITMQ_HOST='"$RABBITMQ_HOST"'/' "$Deploy_path"
要使命令与GNU和BSD Sed一起工作,请指定一个非空选项参数(用于创建备份)并将其直接附加到
-i
:sed -i'.bak' 's/RABBITMQ_HOST=.*/RABBITMQ_HOST='"$RABBITMQ_HOST"'/' "$Deploy_path" &&
rm "$Deploy_path.bak" # remove unneeded backup copy
背景信息,(更多)可移植的解决方案,以及您的命令的精化可以在下面找到。
可选背景信息
听起来像是在使用BSD/macOS
sed
,其-i
选项需要一个option参数,该参数指定要创建的备份文件的后缀。因此,正是您的
sed
脚本(出乎您的意料)被解释为-i
的选项参数(备份后缀),而您的输入文件名被解释为脚本,显然失败了。相比之下,您的命令使用GNU
sed
语法,其中-i
可以自己使用来指示不保留要更新的输入文件的备份文件。等效的BSD
sed
选项是-i ''
-请注意,技术上需要使用单独的参数来指定选项参数''
,因为它是空字符串(如果使用-i''
,shell只需在''
看到它之前删除sed
:-i''
实际上与刚才的-i
相同)。不幸的是,这将不适用于GNU
sed
,因为它只在直接附加到-i
时识别选项参数,并将单独的''
解释为单独的参数,即脚本。这种行为上的差异源于
-i
选项实现背后根本不同的设计决策,并且可能不会因为向后兼容而消失如果不希望创建备份文件,则没有一种
-i
语法对BSD和GNU都有效。有四个基本选项:
(a)如果您知道您将只使用GNU或BSD
sed
,请相应地构造sed
选项:-i
用于GNU-i
,sed
用于BSD-i ''
。(b)指定一个非空后缀作为
sed
的选项参数,如果直接将其附加到-i
选项,则该后缀可用于两种实现;例如,-i
。虽然这总是创建一个后缀为-i'.bak'
的备份文件,但您可以在之后删除它。(c)在运行时确定要处理的
.bak
实现,并相应地构造sed
选项。(d)完全省略
-i
(不符合POSIX),并使用临时文件替换成功时的原始文件:-i
。请注意,这在本质上是
sed '...' "$Deploy_path" > tmp.out && mv tmp.out "$Deploy_path"
在幕后所做的事情,它可能会产生意想不到的副作用,特别是一个输入文件,它是一个符号链接,正在被一个常规文件替换;-i
,但是,它保留了原始文件的某些属性:请参阅我的this answer的下半部分。下面是(c)的一个
-i
实现,它还简化了原始代码(使用两个替换的单bash
调用)并使其更加健壮(变量是双引号):#!/bin/bash
RABBITMQ_HOST='rabbitmq1'
RABBITMQ_PASS='12345'
Deploy_path="test.env"
# Construct the Sed-implementation-specific -i option-argument.
# Caveat: The assumption is that if the `sed` is not GNU Sed, it is BSD Sed,
# but there are Sed implementations that don't support -i at all,
# because, as Steven Penny points out, -i is not part of POSIX.
suffixArg=()
sed --version 2>/dev/null | grep -q GNU || suffixArg=( '' )
sed -i "${suffixArg[@]}" '
s/^\(RABBITMQ_HOST\)=.*/\1='"$RABBITMQ_HOST"'/
s/^\(RABBITMQ_PASS\)=.*/\1='"$RABBITMQ_PASS"'/
' "$Deploy_path"
注意,对于上面为
sed
和$RABBITMQ_HOST
定义的特定值,可以安全地将它们直接拼接到$RABBITMQ_PASS
脚本中,但是如果这些值包含sed
、&
、/
或换行符的实例,则需要在转义之前执行,以免中断\
命令。有关如何执行一般的预转义,请参见我的this answer,但您也可以考虑使用其他工具,例如
sed
和awk
。[1]GNU Sed认为-i选项参数是可选的,而BSD Sed认为它是必需的,这也反映在语法规范中。在相应的
perl
页中:GNU Sed:man
与BSD Sed-i[SUFFIX]
。关于bash - Shell脚本替换文件中的变量-Sed的-i选项错误进行就地更新,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40777195/