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

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

我想替换出现的“|”除了文件的每一行中的最后一个带有空格,使用仅 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

文件输入:

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     

文件输出:

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

答案1

sed ':a;/[|].*[|]/s/[|]/ /;ta' file
  • /[|].*[|]/:如果线路有两根管子,
  • 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

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


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

sed -e ':a' -e '/[|].*[|]/s/[|]/ /;t a' file

答案2

与以下方法不同的方法卡西莫多的显式循环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

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

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

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

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

有趣的是,在显式循环变化中使用反向引用(就像我和 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

它将每一行读取为一组|分隔字段,|在最后一个字段的开头添加一个字符,并使用字段分隔符的空格打印结果记录。

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

awk -F '|' '{ $NF = FS $NF; print }' file

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

awk -F '|' '{ $NF = FS $NF }; 1' file

答案3

使用 Perl 你可以按照以下方式运行一些东西

perl -pe 's/\|(?=.*\|)/ /g'     ex

在哪里:

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

答案4

您可以使用以下几种替代方案。

$ sed -e '
   s/|[^|]*$/\n&/
   s/\n|/\n/
   y/\n|/| /
' file

$ perl -pe 's/\|/ / until tr/|/|/ == 1' file

$ perl -pe 'my $k=tr/|/|/; s/\|/ / while $k-->1' file

相关内容