这是我的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/macOSsed,其-i选项需要一个option参数,该参数指定要创建的备份文件的后缀。
因此,正是您的sed脚本(出乎您的意料)被解释为-i的选项参数(备份后缀),而您的输入文件名被解释为脚本,显然失败了。
相比之下,您的命令使用GNUsed语法,其中-i可以自己使用来指示不保留要更新的输入文件的备份文件。
等效的BSDsed选项是-i ''-请注意,技术上需要使用单独的参数来指定选项参数'',因为它是空字符串(如果使用-i'',shell只需在''看到它之前删除sed-i''实际上与刚才的-i相同)。
不幸的是,这将不适用于GNUsed,因为它只在直接附加到-i时识别选项参数,并将单独的''解释为单独的参数,即脚本。
这种行为上的差异源于-i选项实现背后根本不同的设计决策,并且可能不会因为向后兼容而消失
如果不希望创建备份文件,则没有一种-i语法对BSD和GNU都有效。
有四个基本选项:
(a)如果您知道您将只使用GNU或BSDsed,请相应地构造sed选项:-i用于GNU-ised用于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,但您也可以考虑使用其他工具,例如sedawk
[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/

10-14 19:43
查看更多