替换/删除 Bash 中其他两行之间的行
Posted
技术标签:
【中文标题】替换/删除 Bash 中其他两行之间的行【英文标题】:Replace/Remove lines between two other lines in Bash 【发布时间】:2021-09-22 18:17:29 【问题描述】:我有这个文件,它是一个 Linux 设备树:
# Other content
...
&uart3
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart3>;
status = "okay";
;
# Other content
...
起初我想提取&uart3
和;
之间的行,这是UART3节点,sed
:
sed -n '/^&uart3/,$p;/^;/q' uart3.dts
输出是:
&uart3
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart3>;
status = "okay";
;
我的问题是如何删除 &uart3
和 ;
之间的所有行,或者有没有办法用其他内容替换它们。
我阅读了awk
解决方案关于检测匹配并提高某些标志here。
但我不明白如何实现这一点。
我没有在这里解析设备树,所以不需要 dtc 库,
我仅将文件作为文本文件处理。
由于这会遇到 Yocto 配方,因此解决方案也可以在 Python
中。
【问题讨论】:
您能否更具体地说明您在尝试遵循 awk 方法时遇到了什么问题?你试图做什么,它是如何失败的? (另外,您要针对哪个版本的 awk?POSIX 标准 awk?mawk?gawk?) (一般来说,“我怎样才能跨任何语言做到这一点?”问题很容易被认为过于宽泛而无法成为主题;询问您的具体问题在一个问题中在 sed 中执行此操作,在另一个问题中您在 awk 中执行此操作的特定问题,&c. 将避免这种情况)。 我想在 Bash 或 Python 中使用它,我已经详细说明我需要删除/修改两行之间的任意数量的行,我可以使用两种模式获得。 【参考方案1】:这是我回答的最常见问题的变体,哈哈
$: cat file
# Other content
...
&uart3
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart3>;
status = "okay";
;
# Other content
...
$: cat replacement.txt
This is what I want there instead.
I just stuck it over here in this other file.
$: cat tst
sed '/^&uart3 /,/^;/
/^&uart3 / p; r replacement.txt
/^;/!d;
' file
$: ./tst
# Other content
...
&uart3
This is what I want there instead.
I just stuck it over here in this other file.
;
# Other content
...
/^&uart3 /,/^;/
在从^&uart3
到下一个^;
的匹配范围内的任意行上打开一个块
下一位有一个陷阱 - 不要在文件名之后的同一行中放置任何内容。
/^&uart3 / p; r replacement.txt
这表示如果我们正在查看的行是 ^&uart3
然后打开一个块 -
首先p
rint 它,然后r
ead 进入replacement.txt
的内容。
但是,在大多数实现中,您必须关闭下一行的块,因此它不会认为卷曲是文件名的一部分并破坏语法。
/^;/!d;
表示对于该块的其余部分,除非(!
)它是结束卷曲,只是d
elete 它。
#edit
如果你想在没有文件的情况下这样做 -
$: cat tst
sed '/^&uart3 /,/^;/
/^&uart3 / p; a\
This is what I want there instead.\
There is no other file.
/^;/!d;
' file
$: ./tst
# Other content
...
&uart3
This is what I want there instead.
There is no other file.
;
# Other content
...
参考https://www.gnu.org/software/sed/manual/sed.html#Other-Commands 这一切都在那里。 :)
【讨论】:
如果我想在没有文件的情况下这样做呢? 已编辑。没问题。【参考方案2】:在原生 bash 中:
retrieve_only_section()
local start end reading
start=$1; end=$2; reading=0
while IFS= read -r line; do
if [[ $line = $start ]]; then
reading=1
elif [[ $line = $end ]]; then
reading=0
printf '%s\n' "$line"
continue
fi
(( reading )) && printf '%s\n' "$line"
done
...而retrieve_only_section '&uart3 ' ';' <yourfile
将只检索该部分。
同样,可以将(( reading )) && printf '%s\n' "$line"
替换为(( reading )) || printf '%s\n' "$line"
以仅打印该部分外部的内容。
要替换该部分,可以使用:
replace_section()
local start end replacement in_section
start=$1; end=$2; replacement=$3; in_section=0
while IFS= read -r line; do
if (( ! in_section )) && [[ $line = $start ]]; then
in_section=1
elif (( in_section )) && [[ $line = $end ]]; then
in_section=0
printf '%s\0' "$replacement"
continue
fi
(( in_section )) || printf '%s\n' "$line"
done
【讨论】:
您能否向我解释一下您的第二个代码将如何编辑 &uart3 块并对其进行更改并将其全部放回主文件。 @BelHadjSalemTALEL,这是一个管道。与运行sed <foo > foo.new && mv foo.new foo
相同,使用 replace_section foo bar "$new_value" <foo >foo.new && mv foo.new foo
得到相同的结果
@BelHadjSalemTALEL,请记住,sed -i
和 awk -i
都执行相同的“写入临时文件,然后重命名原始文件”的事情。基本上没有就地重写文件的标准工具,因为这种做法本质上是内存密集型和危险的(当输入流的大小事先不知道时;如果它不知道适合单个write()
系统调用的边界,甚至不能保证任何东西,甚至像原子性这样的远程 - 如果你想保留旧数据以供阅读,你不能截断它,直到你 完成 i> 阅读它,所以输出需要去某个地方)。
运行这两个示例都不起作用,retrieve_only_section
检索了没有;
的块,并且replace_section
从&uart3
替换直到;
之前的最后一行,然后用$replacement离开;
@BelHadjSalemTALEL,请参阅ideone.com/fvhLkm 在线沙箱中的第二个工作。同样,如果您想告诉我某些东西不起作用,那么如果您提供一个沙盒链接会很有帮助,这样我就可以亲眼看到它。【参考方案3】:
如果ed
可用/可接受。
printf '%s\n' '/^&uart3 $/+1;/^;$/-1c' foo . ,p Q | ed -s file.txt
输出
# Other content
...
&uart3
foo
;
# Other content
...
或者使用数组来存储新的数据/输入。
#!/usr/bin/env bash
input_data=(
' foo'
' bar'
' baz'
' more'
' qux'
)
printf '%s\n' '/^&uart3 $/+1;/^;$/-1c' "$input_data[@]" . ,p Q | ed -s file.txt
输出
# Other content
...
&uart3
foo
bar
baz
more
qux
;
# Other content
...
或者为新的输入/数据使用单独的文件
input_file.txt
the
quick
brown
fox
jumps
over
the
lazy
dog
然后
printf '%s\n' '/^&uart3/+1;/^;/-1d' '-1r input_file.txt' ,p Q | ed -s file.txt
输出
# Other content
...
&uart3
the
quick
brown
fox
jumps
over
the
lazy
dog
;
# Other content
...
如果您对输出没有问题,请将Q
更改为w
,然后就地编辑file.txt
。
删除 ,p
以使输出静音。
【讨论】:
ed not found in my build system that isYocto
,我对你的答案投了赞成票,因为它通常在我的主机上工作。但我需要其他解决方案而不是 ed
。
好的,很遗憾听到ed
不可用,希望awk
大师会来救援! :-)【参考方案4】:
A) 用 sed 删除行
sed '5,7d' file
5,7
是一个范围[要删除的行],包括在内,意思是要删除第 5、6、7 行。 d
参数是在 sed 中删除。 以你的例子为例,这将返回以下内容:
&uart3
;
B) 用 sed 替换一行
sed '5s/[a-z].*/stringToReplace/' file
5
选择数字行[5] s
用于替换字符串,[a-z]
用于选择任何小写字母,在本例中为:pinctrl
。 .*
用于选择[整行]。 输出:
&uart3
stringToReplace
pinctrl-0 = <&pinctrl_uart3>;
status = "okay";
;
C) 用另一个文本文件中的sed
替换所有行
sed '5,+2d;4r replace.txt' file
如果您有另一个包含 TEXT 的文件,请使用 r
ARGUMENT replace.txt。
5,+2d
选择行号5
。 +2
选择 5 之后的两行,意思是 5,6,7。d
删除这些行。 ;
用于添加另一个sed
命令[如管道]。 4r
表示选择第 4 行。r
表示替换为文本文件,在本例中为 replace.txt
。 输出
&uart3
replace = line 5
replace = line 6
replace = line 7
;
您需要知道要删除的行数:
grep -n "pinctrl-\|status" file
-n
返回行数。 \|
这是用于 grep 的多个参数。 'example1\|example2\|exmple3'
--> 返回:
【讨论】:
以上是关于替换/删除 Bash 中其他两行之间的行的主要内容,如果未能解决你的问题,请参考以下文章
Notepad++ 将打开文档中的两行替换为其他 (10) 行