while 循环中的 sed 命令不适用于 ubuntu

Posted

技术标签:

【中文标题】while 循环中的 sed 命令不适用于 ubuntu【英文标题】:sed command inside while loop is not working for ubuntu 【发布时间】:2021-10-12 15:17:30 【问题描述】:

我有两个文件;第一个包括我想在第二个文件 (file.cfg) 中搜索的模式 (file.txt)。

一旦在“file.cfg”中找到模式,我想删除它 + 之后的任何内容,直到下一个 Hello 出现在该行的开头。

我已经创建了以下脚本,但它不起作用:

#! /bin/bash
cat file.txt | while read LINE; do
echo $LINE
    sed -i "/^$LINE$/,/^Hello///p;d;" "file.cfg"
    sed -i "/^$LINE$/d" "file.cfg"
done

昨天在测试文件上工作正常,今天我修改了文件名,但它停止工作了。

我不确定我是否错误地更改了某些内容,但如果我将使用 Ubuntu 命令行中的以下内容,它可以工作:

sed -i "/^Hello World$/,/^Hello///p;d;" "file.cfg"

另外,我在循环中添加了echo,可以看到“file.txt”中的每一行

为了提供更多信息,我将举例说明我需要使用此代码实现什么:

“file.txt”包含模式一旦找到模式,我需要在“file.cfg”中找到匹配项,我需要将其删除,然后在下一个 Hello 之前出现任何内容。

sed -i "/^$LINE$/,/^Hello///p;d;" "file.cfg" --> 这行应该删除中间的任何东西。

sed -i "/^$LINE$/d" "file.cfg" --- > 删除模式本身。

+++++++++++

请看下面的例子:

File.cfg 分为多个部分;每个部分都以 Hello 开头

File.txt 包含随机部分名称;我需要一个脚本来从 File.txt 中读取该部分的名称并查看它是否在 file.cfg 中可用,然后删除该部分名称及其所有内容

文件.txt:

Hello World
Hello Mohammad
Hello Scripting

文件.cfg:

Hellow xyz
a
b
c
Hello World
v
b
n
Hello stack
q
w
e

最终结果应该是:

Hellow xyz
a
b
c
Hello stack
q
w
e

找到部分名称后,我需要删除所有内容,直到出现在行首的下一个“Hello”(新部分)。

除了部分名称之外,没有任何行以 Hello 开头。

【问题讨论】:

在循环中使用sed -i 反复重写同一个文件效率低下且容易出错。可能会尝试重构以从您的输入文件创建单个 sed 脚本。并尝试shellcheck.net 向您指出代码中的一些其他常见错误。 请阅读why-is-using-a-shell-loop-to-process-text-considered-bad-practice 以了解您的脚本的一些问题,is-it-possible-to-escape-regex-metacharacters-reliably-with-sed 了解其他问题,并将其复制/粘贴到shellcheck.net 以了解更多信息。 edit您的问题包括简洁、可测试的样本输入和预期输出,以便我们为您提供帮助。 并且在说明匹配文本的要求时不要使用“模式”这个词,因为它非常模棱两可,而是使用 regexp-or-string 加上 full-or-partial。见how-do-i-find-the-text-that-matches-a-pattern。 感谢您提供的文件。你能帮我修复我的代码吗?我还是脚本新手,我应该今天就让它工作。 我们绝对可以帮助您修复您的代码,但我们需要您告诉我们它应该做什么并首先提供一个示例。我们目前甚至不知道 $LINE 是否应该被视为正则表达式或字符串(例如,LINE 中的 a.c 是否应该与 .cfg 文件中的 abc 匹配?)。请edit您的问题,至少将“模式”替换为正则表达式或字符串,并添加示例输入和预期输出,以演示您需要脚本执行的操作,我们可以复制/粘贴以进行测试。 【参考方案1】:
$ awk 'NR==FNRnames[$0]; next $1=="Hello"f=($0 in names) !f' File.txt File.cfg
Hellow xyz
a
b
c
Hello stack
q
w
e

如果您想进行“就地”编辑,那么就像您当前使用的 GNU sed 有 -i,GNU awk 有 -i inplace 但请注意,您正在使用 2 个输入文件,所以您需要写给他们两个:

awk -i inplace 'NR==FNRnames[$0]; print; next $1=="Hello"f=($0 in names) !f' File.txt File.cfg

或者只为第二个激活就地编辑,请参阅 gawk 手册页了解如何控制它。恕我直言,仅使用临时输出文件更简单:

tmp=$(mktemp) &&
awk 'NR==FNRnames[$0]; next $1=="Hello"f=($0 in names) !f' File.txt File.cfg > "$tmp" &&
mv -- "$tmp" File.cfg

【讨论】:

谢谢你的代码,实际上我的文件很大,所以我无法运行命令并验证结果而不创建新文件或覆盖原始文件,我尝试添加 -i inplace但它清除了 File.txt 内容,有什么想法吗? 关于my file is large so... - 在任何大小的文件上运行的任何命令都是如此,你不能verify the result without creating a new file or overwriting the original one 所以我知道你为什么这么说。是的,如果您按原样运行脚本,那么-i inplace 将清除File.txt,因为脚本在读取File.txt 块中的File.txt 时没有打印任何内容。如果要使用-i inplace,请将next 更改为print; next 我在回答中添加了一些关于如何更新 File.cfg 的更多信息。【参考方案2】:

我喜欢@tripleee 的建议,即从模式文件创建一个 sed 脚本。它导致单次传递和 sed 使 sed 吸引我的幽默感 :)

第一步是生成sed脚本:

sed 's|.*|/^&$/, /^Hello/ \n\t/^&$/ d\n\t/^Hello/! d\n|' file.txt
/^Hello World$/, /^Hello/ 
    /^Hello World$/ d
    /^Hello/! d

/^Hello Mohammad$/, /^Hello/ 
    /^Hello Mohammad$/ d
    /^Hello/! d

/^Hello Scripting$/, /^Hello/ 
    /^Hello Scripting$/ d
    /^Hello/! d

简而言之,对于每个地址范围,我们要删除除结束模式之外的所有内容。

我将使用 bash 进程替换生成上述 sed 并将其视为 sed 程序文件(或者可以将其放入临时文件中):

#!/bin/bash

sed -f <(
    sed 's|.*|/^&$/, /^Hello/ \n\t/^&$/ d\n\t/^Hello/! d\n|' file.txt
) file.cfg

我省略了 -i 就地编辑选项进行测试。

对于无损检测,将预期结果与脚本输出进行比较:

diff expect <(./remove.sh) && echo ok

【讨论】:

以上是关于while 循环中的 sed 命令不适用于 ubuntu的主要内容,如果未能解决你的问题,请参考以下文章

sed 命令适用于 Linux,但不适用于 OS X

艾克斯 | sed -i 选项不适用于特定的行号

使用 sed 删除匹配模式不适用于 mac os Catalina

Shell编程Shell中for循环while循环until循环语句

Shell编程Shell中for循环while循环until循环语句

为什么 0适用于该程序但不适用于在子函数中执行while循环的情况? [关闭]