我在 CentOS Bash 环境中工作,并且有一个很长的sed
命令,作为一个不是专业系统管理员并且sed
可能两年使用一次的人,我发现有点“令人困惑”,至少作为一个长命令:
read new_email_address
sed -i 's/$to = ".*";$/$to = "'"$new_email_address"'";/' FILE
我想将sed
命令分解为类似以下嵌套伪代码的内容:
sed -i
'
s/
$to = ".*";$
/
$to = "'"$new_email_address"'";
/g
'
FILE_PATH
答案1
我会用perl
这里。-i
是一个非标准选项,一些sed
实现已经复制了它,perl
但它不可移植。使用sed
这种方式也是一个命令注入漏洞,因为内容$new_email_address
最终被解释为sed
代码(GNU 语言中有一些命令sed
可以运行任意命令,例如尝试/;ereboot;#
在该read
提示符下输入)。
IFS= read -r new_email_address
REPLACEMENT="$new_email_address" perl -pi -e '
s{
(\$to \s* = \s* ") .* (" \s* ; \s* )$
}{$1$ENV{REPLACEMENT}$2}gx
' FILE
在perl
- 您可以使用
s{...}{...}flags
extra ,s/.../.../flags
这样可以更轻松地查看匹配对(并且只要它们匹配,就允许在内部使用 , ){
。}
- 使用该
x
标志,您可以在正则表达式内添加空格(甚至注释),以使其更易于阅读(请注意,这些空格不是正则表达式的一部分,而是\s*
匹配任意数量的空格)。 - 您可以安全地在替换中使用任何任意字符串,甚至可以通过环境变量传递包含
&
、 、反斜杠或换行符的字符串。/
- 只要您不使用
-C
//-Mlocale
...-Mopen=locale
选项,perl
就在字节级别工作,因此.*
即使输入在语言环境中未形成有效文本,它也永远不会失败匹配。 - 与某些
sed
实现相反,perl
对行长度没有限制(可用内存除外),并且不会因包含 NUL 字节或不以换行符结尾的输入而阻塞。
为了在替换部分中也允许空格,您可以添加e
导致替换为perl
代码的标志:
REPLACEMENT="$new_email_address" perl -pi -e '
s{
(\$to \s* = \s* ") .* (" \s* ; \s* )$
}{
$1 . $ENV{REPLACEMENT} . $2
}gxe
' FILE
例如。还要记住,使用read
without 设置$IFS
和 without-r
很少有意义。
答案2
不确定它是否对您来说更具可读性,但您可以sed
首先使用构建表达式printf
,然后将其与以下方式一起使用sed
:
sed_expr=$(printf 's/$to = ".*";$/$to = "%s";/' "$new_email_address")
sed -i "$sed_expr" FILE
这样,在我看来,可以更容易地了解 sed 的整体功能,以及输入在其中到底扮演什么角色。
答案3
引用的命令类似于我在最近的一个回答中写道(已-i
添加):
sed 's/$to = ".*";$/$to = "'"$new_email_address"'";/' file
这是sed
通过单个编辑命令调用的。表达式中使用的命令sed
是s
命令,它执行替换,即将与正则表达式匹配的内容替换为其他内容。
该命令的一般形式s
为range s/pattern/replacement/flags
。在我们在这里处理的命令中,没有range
表达式(该s
命令应用于全部输入文本中的行),并且没有flags
.因此,我们sed
在通用表单上有一个编辑脚本
s/pattern/replacement/
正如您在命令中看到的,该pattern
位是
$to = ".*";$
$to = "
此模式与后跟的文字文本匹配任何事物(任何字符的任何长度的序列),后跟文字文本";
。$
at the end 强制最后一位在行";
的最末端匹配。
然后我们就有了replacement
。
由于您想要替换为取决于 shell 变量值的内容,因此我们必须暂时脱离单引号字符串(即表达式)sed
。我们在之后执行此操作
$to = "
在替换中。 shell 变量的值new_email_address
被插入,并用双引号正确引用,以便 shell 不会将其拆分为空格或对其值执行文件名通配。
插入值后,我们以以下命令结束命令replacement
部分s
";
这是完整的replacement
字段,$to = "
后跟一些值(新的电子邮件地址),然后是";
。
因此,将其分解并明确该命令的每一位的作用和含义:
命令结构sed
:
sed 's/$to = ".*";$/$to = "'"$new_email_address"'";/' file
s/ pattern / replacement /
sed
组成shell 中表达式的字符串位:
sed 's/$to = ".*";$/$to = "'"$new_email_address"'";/' file
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^
single-quoted string double-quoted final single-quoted bit
string for shell
variable expansion
以上以更示意性的方式
sed 'something here'"$variable_value_here"'ending here' file
该something here
位恰好以双引号结尾,并且该ending here
位恰好以双引号开头。
答案4
保持引号分开的一种方法是通过使用多个 -e sed 代码来分隔搜索和替换来破坏 sed 命令。
q=\"; # a double quote character
sed -i \
-e '/$to = ".*";$/c\' \
-e "\$to = $q$new_email_address$q;" \
FILE
sed -i \
-e '/$to = ".*";$/!b' \
-e "s//\$to = $q$new_email_address$q;/" \
FILE;