替换每行中除最后一个字符以外的所有字符

et_*_*ome 15 sed regular-expression

我想替换出现的“|” 除了使用sed 的带有空格的文件每一行中的最后一行。我想避免这样做:

 sed -e "s/[|]/ /1" -e "s/[|]/ /1" -e "s/[|]/ /1" -e "s/[|]/ /1" -e "s/[|]/ /1"  -e "s/[|]/ /1" -e "s/[|]/ /1" mydata.txt
Run Code Online (Sandbox Code Playgroud)

文件输入:

FLD1     |SFK TK |FLD2   |FLD4 |FLD5 |-          |20200515 |NNNN |406   RCO 301
FLD1     |SFK TK |FLD2   |FLD4 |FLD5 |-          |20200515 |NNNN |0
FLD1     |SFK TK |FLD2   |FLD4 |FLD5 |-          |20200515 |NNNN |0     
Run Code Online (Sandbox Code Playgroud)

文件输出:

FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN |406   RCO 301
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN |0
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN |0
Run Code Online (Sandbox Code Playgroud)

Qua*_*odo 20

sed ':a;/[|].*[|]/s/[|]/ /;ta' file
Run Code Online (Sandbox Code Playgroud)
  • /[|].*[|]/: 如果线路有两个管道,
  • s/[|]/ /: 用空格代替第一个。
  • ta: 如果进行了替换,则返回到:a

输出:

$ sed ':a;/[|].*[|]/s/[|]/ /;ta' file
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN |406   RCO 301
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN |0
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN |0
Run Code Online (Sandbox Code Playgroud)

正如@steeldriver 所说,您可以简单地使用|而不是[|]在基本正则表达式 (BRE) 中使用,就像上面的情况一样。如果将-E标志添加到 sed,则启用扩展正则表达式 (ERE),然后您需要编写[|]\|.


只是为了完整性,POSIX sed 规范说“编辑除{...}、a、b、c、i、r、t、w、:# 以外的命令可以后跟分号”。然后,上述的合规替代方案是:

sed -e ':a' -e '/[|].*[|]/s/[|]/ /;t a' file
Run Code Online (Sandbox Code Playgroud)

  • 略有不同,但非常相似:`sed -e ':again' -e 's/|\(.*\)|/ \1|/' -e 't again' file`(也是 POSIX,因为你需要一个通常在标签后换行)。 (2认同)

Kus*_*nda 11

Quasímodo 在 中的显式循环sed不同的方法:

$ sed 'h; s/.*|//; x; s/|[^|]*$//; y/|/ /; G; y/\n/|/' file
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN |406   RCO 301
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN |0
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN |0
Run Code Online (Sandbox Code Playgroud)

对于每一行,这会将行保存在保留空间中h,然后删除行上的所有内容,包括最后一个|. 然后它交换该行的原始副本并删除最后一个|及其后的所有内容。

模式空间现在包含行的原始第一部分,保持空间包含行的最后部分。

第一个y///命令|用空格替换所有剩余的。 G将保留空间附加到模式空间的末尾,中间有一个换行符。第二个y///命令将该换行符转换为 a |,我们就完成了。

进行有限(固定)数量的s///替换并y///在可能的情况下使用更快的命令意味着这比显式循环变化运行得更快(50 MiB 数据上大约 2.3 秒,而使用 GNU 的相同数据上的循环大约为 7.8 秒)sed在我的机器上)。

有趣的是,在显式循环变体中使用反向引用,就像我和 Isaac 所做的那样,会减慢它的速度(Isaac 的变体大约为 33 秒,我的(在评论中)大约为 29秒,在相同的数据集和相同的情况下)条件如上)。


使用awk,这几乎|空格替换了除最后一个之外的所有分隔符。“几乎”,因为它插入前的最后一个空间|

$ awk -F '|' 'BEGIN { OFS = " " } { $NF = "|" $NF; print }' file
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN  |406   RCO 301
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN  |0
FLD1      SFK TK  FLD2    FLD4  FLD5  -           20200515  NNNN  |0
Run Code Online (Sandbox Code Playgroud)

它将每一行作为一组|-delimited 字段读取|,在最后一个字段的开头添加一个字符,并打印结果记录,其中包含字段分隔符的空格。

考虑到的默认行为awk(空格是默认的输出字段分隔符,输入字段分隔符可用作FS):

awk -F '|' '{ $NF = FS $NF; print }' file
Run Code Online (Sandbox Code Playgroud)

或者,稍微短一点,由@Isaac 提供,

awk -F '|' '{ $NF = FS $NF }; 1' file
Run Code Online (Sandbox Code Playgroud)


JJo*_*oao 9

使用 Perl 你可以运行一些类似的东西

perl -pe 's/\|(?=.*\|)/ /g'     ex
Run Code Online (Sandbox Code Playgroud)

在哪里:

  • perl -pe action -- 执行动作并打印
  • \|(?=.*\|)是一个正则表达式,它与包含另一个|未消耗的lookeahed匹配(?=.*|)|


ImH*_*ere 5

可能:

sed -e ':a' -e '/|\(.*|\)/s// \1/;ta' file
Run Code Online (Sandbox Code Playgroud)
  • -e ':a'定义a要跳转的标签 ( )。
  • -e ' 开始另一个脚本部分。
  • /|\(.*|\)/一个正则表达式匹配两个|,中间的所有内容,并捕获中间和最后一个之间的所有内容|
  • s// \1/ 用捕获的内容替换上面匹配的所有内容。
  • ;ta 再次循环。
  • ' file 在给定的文件名上。

要测量所有选项的速度(从快到慢),您可以使用:

sed -e ':a' -e '/|\(.*|\)/s// \1/;ta' file
Run Code Online (Sandbox Code Playgroud)

用作:

$ ./testbash.sh 235000
run    : 0m07.676s sec
run    : 0m17.753s sec
run    : 0m22.074s sec
run    : 0m24.036s sec
run    : 0m24.047s sec
Run Code Online (Sandbox Code Playgroud)