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 删除匹配模式不适用于 mac os Catalina
Shell编程Shell中for循环while循环until循环语句