Shell脚本替换文件中的变量-Sed的-i选项错误进行就地更新 [英] shell script replace variables in file - error with Sed's -i option for in-place updating

查看:615
本文介绍了Shell脚本替换文件中的变量-Sed的-i选项错误进行就地更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的test.env

RABBITMQ_HOST=127.0.0.1
RABBITMQ_PASS=1234

我想用test.shtest.env中的值替换为:

And I want to use test.sh to replace the value in test.env to :

RABBITMQ_HOST=rabbitmq1
RABBITMQ_PASS=12345

这是我的 test.sh

#!/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; dr :

使用 BSD Sed (例如在 macOS 上也可以找到),您必须使用-i ''而不是-i (用于不创建备份文件)以使命令正常工作;例如:

With BSD Sed, such as also found on macOS, you must use -i '' instead of just -i (for not creating a backup file) to make your commands work; e.g.:

sed -i '' 's/RABBITMQ_HOST=.*/RABBITMQ_HOST='"$RABBITMQ_HOST"'/'  "$Deploy_path"

要使命令 GNU和BSD Sed 一起使用,请指定 nonempty 选项参数(会创建一个备份),然后 直接将其附加到-i :

To make your command work with both GNU and BSD Sed, specify a nonempty option-argument (which creates a backup) and attach it directly to -i:

sed -i'.bak' 's/RABBITMQ_HOST=.*/RABBITMQ_HOST='"$RABBITMQ_HOST"'/'  "$Deploy_path" &&
  rm "$Deploy_path.bak" # remove unneeded backup copy

背景信息,(更多)便携式解决方案和命令的改进可以在下面找到.

Background information, (more) portable solutions, and refinement of your commands can be found below.

听起来您正在使用 BSD/macOS sed,其-i选项 requires 的选项参数指定备份文件的后缀为创建.
因此,您的sed 脚本(违反您的期望)被解释为-i option-argument (备份后缀),而您的输入 filename 被解释为 script ,显然会失败.

It sounds like you're using BSD/macOS sed, whose -i option requires an option-argument that specifies the suffix of the backup file to create.
Therefore, it is your sed script that (against your expectations) is interpreted as -i's option-argument (the backup suffix), and your input filename is interpreted as the script, which obviously fails.

相反,您的命令使用 GNU sed语法,其中-i可以单独用于指示输入文件的 no 备份文件要更新.就地保留.

By contrast, your commands use GNU sed syntax, where -i can be used by itself to indicate that no backup file of the input file to updated in-place is to be kept.

等效的 BSD sed选项是-i ''-请注意 technical 需要使用 separate 参数来指定选项-参数'',因为它是空字符串(如果使用了-i'',则外壳程序会在sed看到它之前先剥离'':-i''实际上与-i相同).

The equivalent BSD sed option is -i '' - note the technical need to use a separate argument to specify the option-argument '', because it is the empty string (if you used -i'', the shell would simply strip the '' before sed ever sees it: -i'' is effectively the same as just -i).

遗憾的是,这然后不适用于 GNU sed,因为它仅在直接附加-i时才识别选项参数,并会解释单独的''作为单独的参数,即 script .

Sadly, this then won't work with GNU sed, because it only recognizes the option-argument when directly attached to -i, and would interpret the separate '' as a separate argument, namely as the script.

这种行为上的差异源于实现-i选项背后的根本不同的设计决策,并且由于向后兼容的原因,它可能不会消失. [1]

This difference in behavior stems from a fundamentally differing design decision behind the implementation of the -i option and it probably won't go away for reasons of backward compatibility.[1]

如果您要创建备份文件,则没有一种-i语法可同时用于 BSD和GNU sed.

If you do not want a backup file created, there is no single -i syntax that works for both BSD and GNU sed.

有四个基本选项:

  • (a)如果您知道只使用 GNU BSD sed,请相应地构造-i选项:<对于GNU sed为c4>,对于BSD sed-i ''.

  • (a) If you know that you'll only be using either GNU or BSD sed, construct the -i option accordingly: -i for GNU sed, -i '' for BSD sed.

(b)指定 nonempty 后缀作为-i的选项参数,如果将其直接 附加到-i选项,则将其后缀,可与两者实现一起使用;例如-i'.bak'.尽管这总是创建后缀为的备份文件,但是您可以随后将其删除.

(b) Specify a nonempty suffix as -i's option-argument, which, if you attach it directly to the -i option, works with both implementations; e.g., -i'.bak'. While this invariably creates a backup file with suffix .bak, you can just delete it afterward.

(c)在运行时确定要处理的是哪个sed实现,并相应地构造-i选项.

(c) Determine at runtime which sed implementation you're dealing with and construct the -i option accordingly.

(d)完全省略-i(不兼容POSIX),并使用临时文件成功替换原始文件:sed '...' "$Deploy_path" > tmp.out && mv tmp.out "$Deploy_path".
请注意,这实际上是-i在后台执行的操作,它可能会产生意外的副作用,尤其是输入文件,即 symlink 被替换为 regular 文件;但是,-i确实保留了原始文件的某些属性:请参阅我的此答案的下半部分.

(d) omit -i (which is not POSIX-compliant) altogether, and use a temporary file that replaces the original on success: sed '...' "$Deploy_path" > tmp.out && mv tmp.out "$Deploy_path".
Note that this is in essence what -i does behind the scenes, which can have unexpected side effects, notably an input file that is a symlink getting replaced with a regular file; -i, does, however, preserve certain attributes of the original file: see the lower half of this answer of mine.

这是(c)的bash实现,该实现还简化了原始代码(使用2个替换的单个sed调用)并使它更健壮(变量被双引号):

Here's a bash implementation of (c) that also streamlines the original code (single sed invocation with 2 substitutions) and makes it more robust (variables are double-quoted):

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

请注意,使用上面为$RABBITMQ_HOST$RABBITMQ_PASS定义的特定值,可以将它们直接直接拼接到sed脚本中是安全的,但是如果这些值包含&/\或换行符,则需要先进行转义,以免破坏sed命令.
有关如何执行通用预转义的信息,请参见我的此答案,但此时您还可以考虑使用其他工具,例如为awkperl.

Note that with the specific values defined above for $RABBITMQ_HOST and $RABBITMQ_PASS, it is safe to splice them directly into the sed script, but if the values contained instances of &, /, \, or newlines, prior escaping would be required so as not to break the sed command.
See this answer of mine for how to perform generic pre-escaping, but you may also consider other tools at that point, such as awk and perl.

[1] GNU Sed认为选项参数是-i optional ,而BSD Sed认为它是强制性,这也反映在语法规范中.在相应的man页面中:GNU Sed:-i[SUFFIX]与BSD Sed -i extension.

[1] GNU Sed considers the option-argument to -i optional, whereas BSD Sed considers it mandatory, which is also reflected in the syntax specs. in the respective man pages: GNU Sed: -i[SUFFIX] vs. BSD Sed -i extension.

这篇关于Shell脚本替换文件中的变量-Sed的-i选项错误进行就地更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆