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

查看:59
本文介绍了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,例如在 ma​​cOS 上也可以找到,您必须使用 -i ''code> 而不是 -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,请指定一个非空选项参数(创建备份)并将其直接附加-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 选项 需要 一个选项-argument 指定要创建的备份文件的后缀.
因此,您的 sed script(与您的预期相反)被解释为 -ioption-argument(备份后缀),并且您输入的文件名被解释为脚本,这显然失败了.

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 '' - 注意技术需要使用 separate 参数来指定选项参数 '',因为它是空字符串(如果你使用了 -i'',shell 会简单地在 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 相应的选项:-i 用于 GNU sed-i '' 用于 BSD sed>.

  • (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) 指定一个非空后缀作为-i的选项参数,如果你直接附加它到 -i 选项,适用于 both 实现;例如,-i'.bak'.虽然这总是会创建一个后缀为 .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 被替换为 常规文件;-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 认为 option-argument 为 -i optional,而 BSD Sed 认为它mandatory,这也反映在语法规范中.在各自的 man 页面中:GNU Sed:-i[SUFFIX] vs. 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天全站免登陆