【问题标题】:awk function to modify several columns with regex in a csvawk 函数在 csv 中使用正则表达式修改多个列
【发布时间】:2020-05-10 17:47:30
【问题描述】:

目标:

我需要修改网址以仅保留其中的数字(纬度/经度/id): 在 .csv 文件中,我有一个“标题中的某些标题”。这个我需要找到。在这个找到的标题栏中,我需要删除网址的开头和结尾,所以它只留下一个数字,这是网址的一部分。我需要在不同的结构化 csv 上执行此操作,其中包含具有不同标题和不同 url 模式的几列。有没有办法用 awk 在 bash 中编写函数?

我试过了 - 它没有工作,因为它缺少很多缺失的知识:

#!/bin/bash
CSVFILE=$(find ./aufzubereiten -type f ! -name ".DS_Store") #only one file in this folder.
FILENAME=$(basename "$CSVFILE")

function modify_col() {
    COL= how to find the right column in the csv?
    awk -F',' OFS="," -v pat='"$PAT"' '{sub(/pat/,X,$${COL})} 1' "$CSVFILE" > "$CSVFILE".tmp1 && mv "$CSVFILE".tmp1 "$CSVFILE"
}

COLTITEL="certain Titel in Header"
PAT='/Text1234Text[0-9]{5,8}Text1.html'
PATNEW=''
modify_col

COLTITEL="certain Titel2 in Header"
PAT='/Text2234Text[0-9]{5,8}Text2.html'
PATNEW=''
modify_col

COLTITEL="certain Titel3 in Header"
PAT='/Text3234Text[0-9]{5,8}Text3.html'
PATNEW=''
modify_col

示例文件:

header1, header2, certain Titel in Header, certain Titel2 in Header, certain Titel3 in Header
,,/Text2234Text7846641Text.html,/Text2234Text8974341Text2.html,/Text2234Text823241Text3.html
,,/Text2234Text7846642Text.html,/Text2234Text8974342Text2.html,/Text2234Text823242Text3.html
,,/Text2234Text7846643Text.html,/Text2234Text8974343Text2.html,/Text2234Text823243Text3.html

结果应该是:

header1, header2, certain Titel in Header, certain Titel2 in Header, certain Titel3 in Header
,,7846641,8974341,823241
,,7846642,8974342,823242
,,7846643,8974343,823243

感谢您的想法:)

【问题讨论】:

  • 数据是否真的由文件名中出现 3 次的文字 Text 组成?如果“是”,有一些(相对)简单的解决方案(见下文);如果 real 文件名由不同的文本字符串组成,那么我们需要查看一些真实文件名以及有关如何解析所述名称的更多详细信息
  • @markp 你是对的。我不清楚。非常感谢您的努力和想法。在阅读并理解了一些答案之后,我意识到我应该提到在不同的行中是相同的文本,不应该被触摸。这就是为什么我试图“找到”正确的专栏。
  • 是的,如果没有可靠的示例(输入和输出),我们会留下很多解释,这就是创建 How to create a minimal, reproducible example 的原因之一 :-)

标签: regex bash macos csv awk


【解决方案1】:

您能否尝试使用所示示例进行以下、编写和测试。

awk '
BEGIN{
  FS=OFS=","
}
FNR==1{
  print
  next
}
{
  for(i=1;i<=NF;i++){
    sub(/^\/Text[0-9]+Text/,"",$i)
    sub(/Text.*/,"",$i)
  }
}
1
'  Input_file

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

awk '
BEGIN{                                 ##Starting BEGIN section of code here.
  FS=OFS=","                           ##Setting FS and OFS to comma here.
}
FNR==1{                                ##Checking condition if FNR==1 then do following.
  print                                ##Printing the current line here.
  next                                 ##next will skip all further statements from here.
}
{
  for(i=1;i<=NF;i++){                  ##Starting a for loop to traverse into all fields here.
    sub(/^\/Text[0-9]+Text/,"",$i)     ##Substituting from starting Text digits Text with NULL in current field.
    sub(/Text.*/,"",$i)                ##Substituting everything from Text to till last of field value with NULL in current field.
  }
}
1                                      ##1 will print edited/non-edited line here.
'  Input_file                          ##Mentioning Input_file name here.

【讨论】:

    【解决方案2】:

    假设:

    • 数据看起来完全类似于问题中的示例,即文字 Text 出现在每个 html 文件名中的 3 个位置

    样本数据:

    $ cat text.dat
    header1, header2, certain Titel in Header, certain Titel2 in Header, certain Titel3 in Header
    ,,/Text2234Text7846641Text.html,/Text2234Text8974341Text2.html,/Text2234Text823241Text3.html
    ,,/Text2234Text7846642Text.html,/Text2234Text8974342Text2.html,/Text2234Text823242Text3.html
    ,,/Text2234Text7846643Text.html,/Text2234Text8974343Text2.html,/Text2234Text823243Text3.html
    

    一个awk解决方案:

    $ awk -F"Text" '
    BEGIN  { OFS="," }
    FNR==1 { print ; next }
           { print ",,"$3,$6,$9 }
    ' text.dat
    

    地点:

    • -F"Text" - 使用 Text 作为我们的输入字段分隔符
    • OFS="," - 设置输出字段分隔符
    • FNR==1 {print ; next} - 对于第 1 行(标题行)打印整行并跳到文件中的下一行
    • print ",,"$3,$6,$9 - 打印 2 个逗号,然后是字段 3、6 和 9(由 OFS="," 分隔)

    结果:

    header1, header2, certain Titel in Header, certain Titel2 in Header, certain Titel3 in Header
    ,,7846641,8974341,823241
    ,,7846642,8974342,823242
    ,,7846643,8974343,823243
    

    【讨论】:

      【解决方案3】:

      这是一个通用的解决方案,用于查找具有五位或更多位的数字,并删除其他所有内容。

      awk -F , 'BEGIN { OFS=FS }
        FNR>1{
          for(i=1;i<=NF;++i) {
              gsub(/(^|[^0-9])[0-9]{1,4}([^0-9]|$)/, "", $i);
              gsub(/[^0-9]+/, "", $i);
          }
        } 1' filename
      

      如果您只有一个文件名,则可能没有理由使用find。如果您不知道文件名但当前目录中只有一个文件,* 将扩展为该文件名。

      这有点脆弱,如果一个字段中的两个数字被一个非数字字符分隔,它将不会做正确的事情。解决这个问题并不难,但我很懒,你的要求有点模糊。

      【讨论】:

        【解决方案4】:

        我知道 OP 询问是否有使用 awk 的方法,但从提供的上下文中,我了解到任何可以在 bash 脚本中运行的解决方案都可以解决 OP 的问题。

        对于这种情况,我相信sed 是一个更优雅的解决方案:

        sed 's/[^,]\+[^0-9]\([0-9][0-9]\+\)[^,]\+/\1/g' data.csv
        

        它输出任何接近字段末尾的 2 位或更多位数字。 sed 的扩展版本可能有助于更好地可视化它:

        sed -E 's/[^,]+[^0-9]([0-9][0-9]+)[^,]+/\1/g' data.csv
        

        输出:

        rvb@ubuntu:~$ sed -E 's/[^,]+[^0-9]([0-9][0-9]+)[^,]+/\1/g' data.csv
        header1, header2, certain Titel in Header, certain Titel2 in Header, certain Titel3 in Header
        ,,7846641,8974341,823241
        ,,7846642,8974342,823242
        ,,7846643,8974343,823243
        

        【讨论】:

          猜你喜欢
          • 2012-03-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-02-07
          • 1970-01-01
          • 1970-01-01
          • 2014-04-14
          相关资源
          最近更新 更多