使用 sed 在 linux 中查找以开头并替换的行 [重复]

Posted

技术标签:

【中文标题】使用 sed 在 linux 中查找以开头并替换的行 [重复]【英文标题】:Find line starts with and replace in linux using sed [duplicate] 【发布时间】:2018-10-09 02:03:36 【问题描述】:

如何查找行开头并替换完整行?

文件输出:

 xyz
 abc
 /dev/linux-test1/

代码:

output=/dev/sda/windows
sed 's/^/dev/linux*/$output/g' file.txt

我遇到以下错误:

 sed: -e expression #1, char 9: unknown option to `s'

替换后预期的文件输出:

 xyz
 abc
 /dev/sda/windows

【问题讨论】:

Replace whole line containing a string using sed、Replace whole line when match found with sed、Use slashes in sed replace 和 Delete using a different delimiter with Sed 的可能重复项 【参考方案1】:

让我们一步一步来。

首先我们尝试将“dev”更改为“other”:

sed 's/dev/other/' file.txt
/other/linux-test1/

(省略其他行。)到目前为止,一切都很好。现在 "/dev/" => "/other/":

sed 's//dev///other//' file.txt
sed: 1: "s//dev///other//": bad flag in substitute command: '/'

啊,很困惑,我们将“/”用作命令分隔符和文字文本。所以我们使用不同的分隔符,比如'|':

sed 's|/dev/|/other/|' file.txt
/other/linux-test1/

很好。现在我们尝试替换整行:

sed 's|^/dev/linux*|/other/|' file.txt
/other/-test1/

它并没有替换整行...啊,在 sed 中,'*' 表示 前一个字符重复了任意次数。 所以我们在它前面加上 '.',这意味着任何字符:

sed 's|^/dev/linux.*|/other/|' file.txt
/other/

现在介绍变量:

sed 's|^/dev/linux.*|$output|' file.txt
$output

shell 没有扩展变量,因为单引号。我们改为双引号:

sed "s|^/dev/linux.*|$output|" file.txt
/dev/sda/windows

【讨论】:

【参考方案2】:

这可能对你有用(GNU sed):

output="/dev/sda/windows"; sed -i '\#/dev/linux.*/#c'"$output" file

设置shell变量并将/dev/linux.*/寻址的行改为它。

注意shell 变量需要插值,因此; 即变量可以单独设置在一行上。此外,sed 地址的分隔符必须更改,以免干扰地址,因此\#...#,最后 shell 变量应该用双引号括起来以允许完全插值。

【讨论】:

【参考方案3】:

我建议不要这样做。原因如下。

Sed 不是一种编程语言。它是一个流编辑器,具有一些外观和行为类似于语言的结构,但它在任意字符串操作、格式控制等方面提供的功能很少。

Sed 仅从文件或标准输入(也是文件)中获取数据。在您的 sed 脚本中嵌入字符串是在要求错误 - 像 s/re/$output/ 这样的构造注定会在某个时候失败,几乎不管您在 sed 脚本中构建了什么变通方法。使此类 sed 命令发挥作用的最佳解决方案是在 sed 外部进行输入清理。

这让我想到......这可能是这项工作的错误工具,或者可能只是该工作工具集的一个组件。

您遇到的错误显然是因为您使用的 sed 命令被严重破坏了。替换命令是:

s/pattern/replacement/flags

但你正在运行的命令是:

s/^/dev/linux*/$output/g

您要搜索的模式是^,即行首的空值。你的替换模式是dev,那么你有一堆可能被解释为标志的文本。当您的搜索字符串包含与您用作替代命令选项分隔符的相同字符时,这显然不起作用。

在正则表达式和 sed 中,您可以转义。尽管s/^\/dev\/linux.*/$output/ 可能会吸引您,但如果$output 包含斜线,您仍然会遇到困难。如果您将此脚本从 bash 提供给 sed,则可以使用 $output//\//\\\/,但您无法在 sed 本身中处理这些转义。 Sed 没有变量。

在适当的编程语言中,您可以更好地分离变量内容和用于替换的命令。

output="/dev/sda/windows"
awk -v output="$output" '$1~/\/dev\/linux/  $0=output  1' file.txt

请注意,我在这里使用了$1,因为在您的问题中,您的输入行(和输出)似乎在每行的开头都有一个空格。分配字段(位置)变量时,awk 会自动修剪前导和尾随空格。

或者您甚至可以在纯 bash 中执行此操作,无需使用外部工具:

output="/dev/sda/windows"
while read -r line; do
  [[ "$line" =~ ^/dev/linux ]] && line="$output"
  printf '%s\n' "$line"
done < file.txt

面对领先的空白,这个没有弹性。盐调味。

所以 .. 是的,你可以用 sed 做到这一点。但是,在 sed 中将命令组合在一起的方式使这样的事情变得有风险,尽管有可用的变通方法,例如将替换命令分隔符切换到另一个字符,但几乎可以肯定,使用其他工具会更好。

【讨论】:

以上是关于使用 sed 在 linux 中查找以开头并替换的行 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

bash:字符串查找

查找并替换所有以 # 开头的单词,并将标签文本包装在 HTML 中

Linux批量查找与替换

sed命令在文件中替换、插入

linux sed 如何替换字符串中的反斜杠\

Linux命令之sed批量替换字符串操作