sed用于查找和替换两个字符串之间的字符



我有一个管道分隔文件,其中一列中的某些值/记录在值本身中包含管道,使其看起来像是有比实际更多的列-请注意"第8列"(粗体)如何在"中间"包含管道。这实际上应该显示为"|col u lm n8|",并用空格代替管道。

column1|column2|column3|column4|column5|column6|column7|**col|u|lm|n8**|2016|column10|column11|column12|column13|column14|

我需要用空格替换第8列中的这些管道。

好在第7列和第9列(|2016)中的数据在整个文件中是相同的,所以我可以做一个sed,比如这个

sed 's/|/ /7g;s/.(|2016)/|1/' 

但是,这将更改从第7个管道到管线末端的所有管道。我的问题是,如何将所有管道更改为第7个管道之后的空间,直到"|2016"列?

感谢

对于您的示例输入,这对我来说适用于GNU sed 4.2.2.:

sed -r ':start s/(column7.)([^|]*?)|(.*?.2016)/12 3/; t start' file

它替换column7..2016之间的管道,每次一个管道。成功替换后,tgotos返回:start标签进行另一次替换尝试。

以下是perl解决方案,即使|2016再次出现在行中,它也适用于这种情况

cat file
column1|column2|column3|column4|column5|column6|en|col|u|lm|n8|2016|column10|column11|2016|
perl -pe 's/(en|[^|]*|(?<!^)G[^|]*)|(?!2016)/$1 /g' file
column1|column2|column3|column4|column5|column6|en|col u lm n8|2016|column10|column11|2016|

此正则表达式使用PCRE构造G,它断言上一个匹配的末尾或第一个匹配的字符串的开头的位置。

RegEx演示

在Lars提供的基础上,以下内容应适用于所有版本的sed:

sed -e ':b' -e 's/(|column7|)(.*)|(.*|2016|)/12 3/' -e 'tb' inputfile

这是通过重复替换嵌入的分隔符来实现的,直到找不到替换模式为止。Sed的t命令仅在上一次替换成功的情况下分支到:b标签。

我们使用更经典的BRE,既为了兼容性,也为了避免sed将垂直条解释为ERE中的"或"分隔符。

sed脚本被分成单独的-e选项,因为一些sed变体要求标签引用"在行的末尾",并且-e的参数的终止被认为等同于行的末尾。(GNU sed不需要这个,但其他一些sed需要。)

但正如anubhava在评论中指出的那样,这是一种较差的方法,因为如果输入数据在第9列右侧的某个位置包括第二个2016|,那么它将失败。

如果您正在运行bash,另一种解决方案可能是将字段放入数组中,然后合并元素:

#!/usr/bin/env bash
input="column1|column2|column3|column4|column5|column6|column7|**col|u|lm|n8**|2016|column10|column11|column12|column13|column14|"
IFS=| read -a a <<< "$input"
while [ "${a[8]}" != "2016" ]; do
  a[7]="${a[7]} ${a[8]}"   # merge elements
  unset a[8]               # delete merged element
  a=( "${a[@]}" )          # renumber array
done
printf "%s|" "${a[@]}"

请注意,bash数组默认从索引0开始。readarray内建允许您为索引(-O)指定一个备用起点,但该内建是从bash版本4开始的,还有很多版本3。所以为了便携性,read -a是。

还要注意的是,如果由于某种原因,您的输入数据中没有"2016"字段,那么在没有进一步错误检查的情况下,上述脚本将进入一个无休止的循环。:-)

这个问题我真的很感兴趣,我投了赞成票,但在sedawk 中没有解决

我在python中尝试过并成功了。我不提供official answer,但提供了一些想法:)

$cat sample.csv
column1|column2|column3|column4|column5|column6|column7|col|u|lm|n8|2016|column10|column11|column12|column13|column14|

我的代码:

$cat test.py                                                                                                                                                                           
import re
REGEX = ur"column7|(.+?)|2016+?"
with open("sample.csv", "r") as inputs:
    for line in inputs:
        matches = re.findall(REGEX, line)
        column8 = matches[0]
        new_column8 = column8.replace("|", "")
        print line.replace(column8, new_column8)

结果:

$python test.py                                                                                                                                                                       
column1|column2|column3|column4|column5|column6|column7|colulmn8|2016|column10|column11|column12|column13|column14|

使用GNU awk进行第三个参数匹配():

$ awk 'match($0,/(([^|]*[|]){7})(.*)(|2016|.*)/,a){gsub(/|/," ",a[3]); $0=a[1] a[3] a[4]} 1' file
column1|column2|column3|column4|column5|column6|column7|**col u lm n8**|2016|column10|column11|column12|column13|column14|

当文件只有一行时,您可以col8=$(sed的/([^|]|){7}(.)|2016./\2/'文件)echo"调试行:col8=${col8},已修复${col8//|/}"sed的/^(([^|]|){7}).*|2016/\1'"${col8//|/}"'|2016/'文件

当您知道一个唯一的字符或字符串时,您可以对具有更多行的文件执行同样的操作。我将使用mk97作为唯一字符串:

这可能对你有用(GNU sed):

sed 's/|/&n/7;:a;ta;s/n(|2016|)/1/;s/n|/ n/;ta;s/n(.)/1n/;ta' file

在字段八的开头添加一行换行符。如果换行符出现在第九字段之前,则删除它。如果换行符后面跟着|,则用空格替换|,并在字符上打乱换行符。如果换行符后面没有|,则在字符上打乱换行符。

注意:在到位置保持器:a的任何成功的替换循环上。

最新更新