我想仅在匹配特定字符串后在文件内添加时间戳:
时间戳格式:03_2016
文件包含“$channel_name 政策“字符串,我想用”替换这个字符串$channel_name Policy_03_2016”
我想添加时间戳的文件内容:
package_path=/LSB/Packages/Policy
channel_path=/LSB/Channels/$parent_channel_name/$channel_name Policy
erratum_path=/LSB/Errata/$erratum_type Policies/$erratum_name
errata_path =/LSB/Errata/$parent_channel_name/$channel_name Advisory Roll-Up Policy
package_path=/LSB/Packages/Unapproved/$channel_name
所需文件格式:
package_path=/LSB/Packages/Policy
channel_path=/LSB/Channels/$parent_channel_name/$channel_name Policy_03_2016
erratum_path=/LSB/Errata/$erratum_type Policies/$erratum_name
errata_path =/LSB/Errata/$parent_channel_name/$channel_name Advisory Roll-Up Policy
package_path=/LSB/Packages/Unapproved/$channel_name
现在我正在使用以下命令将时间戳添加到文件:
sed -i "s/$channel_name Policy/$channel_name Policy_$(date +%m_%Y)/g" filename
但这个命令正在替换所有出现的政策和政策_03_2016 像这样:
package_path=/LSB/Packages/Policy_03_2016
channel_path=/LSB/Channels/$parent_channel_name/$channel_name Policy_03_2016
erratum_path=/LSB/Errata/$erratum_type Policies/$erratum_name
errata_path =/LSB/Errata/$parent_channel_name/$channel_name Advisory Roll-Up Policy_03_2016
package_path=/LSB/Packages/Unapproved/$channel_name
这个问题有什么解决办法吗?
答案1
$
对于双引号内的 shell 来说是特殊的,因为它用于参数扩展(如$var
, $1
, $channel_name
)、算术扩展(如$((1+1))
)和命令替换(如$(cmd)
您是实际上在这里使用)。
$
在正则表达式中也很特殊(例如在命令的左侧s/regex/replacement/
sed
),因为它是表示“主题结束”的运算符,除非您使用-E
/-r
来启用扩展正则表达式,它并不特殊,除非 位于$
正则表达式本身的末尾或后跟\)
(或\|
在支持的情况下)。尽管如此,逃避它仍然是一个好习惯。
双引号内的 shell 及其sed
正则表达式都支持\
作为转义运算符来删除字符的特殊含义。\
在这两种情况下也可以逃脱自身。
所以,在这里,你可以这样做:
sed -i "s/\\\$channel_name Policy/\$channel_name Policy_$(date +%m_%Y)/g" filename
也就是说,\\\$
要逃脱 \
本身和\$
外壳,以便\$
被传递到再次逃逸的sed
地方。右侧就足够了,因为命令的替换部分并不特殊。\
$
\$
$
s/regexp/replacement/
虽然不是 POSIX,但这也可以在我知道的任何 shell 中工作:
sed -i "s/[$]channel_name Policy/&_$(date +%m_%Y)/g" filename
[$]
是另一种方式逃脱 $
在正则表达式中。[...]
匹配于放字符,这里仅包含一个:$
。虽然 POSIX 未指定 shell [$]
,但我不知道有哪个 shell 不保持原样。不过,您需要[\$]
该 shell 代码兼容 POSIX。
我们还在&
替换中使用它代表正则表达式匹配的字符串。
另一种选择是使用单引号对于大多数字符串(其中$
并不特殊),以及命令替换的双引号,仍然$
在正则表达式中转义,即使它不是绝对必要的:
sed -i 's/\$channel_name Policy/&_'"$(date +%m_%Y)/g" filename
请注意,您需要$(...)
使用双引号,否则将对其应用 split+glob。
答案2
正如“don_crissti”所评论的,您必须转义美元符号,否则它会尝试获取可变内容(在您的情况下为空)。
或者,如果您使用与“/”不同的分隔符会更安全,因为文件中也有斜杠。但是,只要您不在替换表达式中使用它们,这对于您的情况来说不是问题。
我的工作解决方案:
sed -i 's!\$channel_name Policy!&_'$(date +%m_%Y)'!g' policies.txt
第二个可能的改进是在替换中使用 the,&
因为它正是搜索到的表达式加上一些额外的内容。
第三,在我的测试中,我只能在表达式$(date +%m_%Y)
之外sed
(用单引号括起来)时使命令起作用。
我也尝试过 in GNU sed
(上面的行)和 with ,OSX sed
但没有一个能够正确解释$(command)
.