将整列替换为所有多行中的新值

Replace whole column with a new value in all multiple lines

提问人:Sam 提问时间:11/14/2023 最后编辑:Arvind Kumar AvinashSam 更新时间:11/16/2023 访问量:136

问:

我的文件中有以下几行

$ cat test.txt
69|1074330570|1,sip:+121345633210x3Bverstat=TN-Validation-Passed|tel:+12134565534|0
69|1077822111|2,;tel:+2223120011~sip:[email protected];|sip:[email protected]|0
69|1077988012|1,sip:+121510016070x3Bverstat=TN-Validation-Passed|tel:+136965339510x3Bnpd|0

我想将文件中的第三列和第四列替换为电话号码,如下所示:

69|1074330570|2134563321|2134565534|0
69|1077822111|2223120011|3123120022|0
69|1077988012|2151001607|3696533951|0

好的部分是所有文件在第三列和第四列中都有一个“+”。现在困难的部分是,有时我们会得到 11 位数字,如第一行第三列 (121345633210) 中所注意到的那样,有时在“+”之后没有添加“1”。因此,如果加号后有 1,则排除 1。否则,请在“+”号之后开始长度。同样,如果有 11 位数字,则只需子字符串 10。此外,如果像第 2 行这样的数字超过 1 个,我需要像 2223120011 而不是 2223120051一样选择“+”号后面的第一个数字。

我尝试了以下方法

awk -F"|"  'BEGIN {FS="+"}{print substr($3,2,10)}' test.txt

但它为第二行提供了不正确的值,因为它需要在“+”号之后开始。

正则表达式 linux awk 替换 substr

评论


答:

4赞 markp-fuso 11/14/2023 #1

当前代码的问题:

  • -F"|"两者都定义了输入字段分隔符,因此,从技术上讲,您应该选择一个或另一个,但不能同时选择两者;在这种情况下优先(即被忽略)FS="+"FS="+"-F"|"
  • 由于总体要求是“替换”第 3 列和第 4 列,因此您需要保留 ,但也将其定义为输出字段分隔符(建议:-F"|"BEGIN { FS=OFS="|"}
  • 然后考虑使用字符串函数的某种组合(例如、、、、awksplit()match()index()substr())

假设/理解:

  • 第 3/4 个字段始终具有+
  • 后面的字符串始终是 10 位或 11 位数字+
  • 如果第 3/4 个字段有多个字段,那么我们只对第一个字段感兴趣(例如,我们不必根据 或++telsip)
  • 看来我们正在处理北美电话号码格式(例如,我们不必担心其他国家/地区的不同电话号码格式)

一个想法:awk

awk '
BEGIN { FS=OFS="|" }
      { for (i=3; i<=4; i++) {                         # loop through 3rd and 4th fields
            split($i,a,"+")                            # split on "+"
            d1 = substr(a[2],1,1)                      # get 1st digit after the "+"
            $i = substr(a[2],(d1==1 ? 2 : 1),10)       # redefine ith field based on value of 1st digit
        }
      }
1                                                      # print current line
' test.txt

笔记:

  • 此逻辑是基于我们只需要处理北美电话号码格式(10 位/11 位,国家/地区代码 = 1)的假设进行硬编码的

这将生成:

69|1074330570|2134563321|2134565534|0
69|1077822111|2223120011|3123120022|0
69|1077988012|2151001607|3696533951|0
5赞 anubhava 11/14/2023 #2

您可以使用以下解决方案:awk

awk '
function extphone(s) {
   sub(/^[^+]*\+1?/, "", s)  # remove all text before + then + and optional 1
   return substr(s, 1, 10)   # extract first 10 characters now
}
BEGIN {FS=OFS="|"}
{
   $3 = extphone($3)
   $4 = extphone($4)
} 1' file

69|1074330570|2134563321|2134565534|0
69|1077822111|2223120011|3123120022|0
69|1077988012|2151001607|3696533951|0
3赞 Ed Morton 11/14/2023 #3

使用 GNU awk 用于:gensub()

$ awk '
    BEGIN { FS=OFS="|" }
    {
        for ( i=3; i<=4; i++ ) {
            $i = gensub(/[^+]+\+1?([0-9]{10}).*/,"\\1",1,$i)
        }
        print
    } 
' test.txt
69|1074330570|2134563321|2134565534|0
69|1077822111|2223120011|3123120022|0
69|1077988012|2151001607|3696533951|0

或使用任何 awk:

$ awk '
    BEGIN { FS=OFS="|" }
    {
        for ( i=3; i<=4; i++ ) {
            if ( match($i,/\+1?[0-9]{10}/) ) {
                $i = substr($i,RSTART+RLENGTH-10,10)
            }
        }
        print
    }
' test.txt
69|1074330570|2134563321|2134565534|0
69|1077822111|2223120011|3123120022|0
69|1077988012|2151001607|3696533951|0
2赞 RavinderSingh13 11/15/2023 #4

对于您展示的示例,请尝试以下 GNU 代码。awk

awk '
match($0,/^([^|]*\|[^|]*\|)[^+]*\+1*([0-9]+)[^|]*(\|)[^+]*\+1*([0-9]+)[^|]*(\|.*$)/,arr){
  sub(/0$/,"",arr[2])
  sub(/0$/,"",arr[4])
  print arr[1] arr[2] arr[3] arr[4] arr[5]
}
'  Input_file

解释:为上述代码添加详细说明。

  • 使用 GNU 的函数在其中使用正则表达式,这会创建捕获组,稍后我们可以使用这些来获取所需的输出。awkmatch
  • 使用正则表达式 inside 函数。^([^|]*\|[^|]*\|)[^+]*\+1*([0-9]+)[^|]*(\|)[^+]*\+1*([0-9]+)[^|]*(\|.*$)match
  • 如果找到正则表达式匹配项,则使用 此处 删除第 2 个和第 4 个捕获组的最后部分。0sub
  • 然后打印所有 5 个捕获组以获得所需的输出。