替换/删除 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
...

起初我想提取&amp;uart3;之间的行,这是UART3节点,sed

sed -n '/^&uart3/,$p;/^;/q' uart3.dts

输出是:

&uart3 
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_uart3>;
    status = "okay";
;

我的问题是如何删除 &amp;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
...

/^&amp;uart3 /,/^;/ 在从^&amp;uart3 到下一个^; 的匹配范围内的任意行上打开一个块 下一位有一个陷阱 - 不要在文件名之后的同一行中放置任何内容。

      /^&uart3 / p; r replacement.txt
                 

这表示如果我们正在查看的行是 ^&amp;uart3 然后打开一个块 - 首先print 它,然后read 进入replacement.txt 的内容。 但是,在大多数实现中,您必须关闭下一行的块,因此它不会认为卷曲是文件名的一部分并破坏语法。

/^;/!d; 表示对于该块的其余部分,除非!)它是结束卷曲,只是delete 它。


#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 '&amp;uart3 ' ';' &lt;yourfile 将只检索该部分。

同样,可以将(( reading )) &amp;&amp; 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 &lt;foo &gt; foo.new &amp;&amp; mv foo.new foo 相同,使用 replace_section foo bar "$new_value" &lt;foo &gt;foo.new &amp;&amp; mv foo.new foo 得到相同的结果 @BelHadjSalemTALEL,请记住,sed -iawk -i 都执行相同的“写入临时文件,然后重命名原始文件”的事情。基本上没有就地重写文件的标准工具,因为这种做法本质上是内存密集型和危险的(当输入流的大小事先不知道时;如果它不知道适合单个write() 系统调用的边界,甚至不能保证任何东西,甚至像原子性这样的远程 - 如果你想保留旧数据以供阅读,你不能截断它,直到你 完成 i> 阅读它,所以输出需要去某个地方)。 运行这两个示例都不起作用,retrieve_only_section 检索了没有; 的块,并且replace_section&amp;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 is Yocto ,我对你的答案投了赞成票,因为它通常在我的主机上工作。但我需要其他解决方案而不是 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) 行

Bash:使用另一个文件的行查找和替换文件中的行

Bash:从脚本中查找并替换文本

bash 脚本在 y 出现时找到计数高于 x 的行,然后用 z 替换某个点之后的任何内容

仅用连字符替换单词之间的空格并删除所有其他空格[重复]

如何使用 bash 删除和替换终端中的最后一行?