【问题标题】:read file and extract variables based on what is in the line读取文件并根据行中的内容提取变量
【发布时间】:2019-02-09 02:43:51
【问题描述】:

我有一个如下所示的文件:

$ cat file_test
garbage text A=one B=two C=three D=four
garbage text A= B=six D=seven
garbage text A=eight E=nine D=ten B=eleven

我想遍历每一行并提取特定的“变量”以在循环中使用。如果一行没有变量,则将其设置为空字符串。

因此,对于上面的示例,假设我想提取变量 ABC,然后对于每一行,循环将具有以下内容:

  1. garbage text A=one B=two C=three D=four
    • A = "一个"
    • B = "两个"
    • C = “三”
  2. garbage text A= B=six D=seven
    • A = ""
    • B = “六”
    • C = ""
  3. garbage text A=eight E=nine D=ten B=eleven
    • A = “八”
    • B = “十一”
    • C = ""

我最初的计划是使用sed,但这不起作用,因为“变量”的顺序不一致(例如最后一行)并且可能缺少“变量”(例如第二行)。

我的下一个想法是逐行浏览,然后使用awk 将行拆分为字段并根据每个字段设置变量,但我不知道从哪里开始或如何开始。

我愿意接受其他想法或更好的建议。

【问题讨论】:

  • 好的...一旦你做了变量,然后呢?你至少应该表现出你的努力。
  • 我还没有走到那一步。我不知道如何提取变量。
  • @IMTheNachoMan,从您的帖子中,我了解到您需要获取字符串的值并将它们用作您进一步任务的 shell 变量,对吗?或者您想简单地打印它们的值?
  • @IMTheNachoMan :如果您真的想在 bash 中执行此操作(IMO 由 tshiono 发布的 Perl 解决方案更具吸引力),我会使用嵌套循环:外部循环遍历行。内部循环遍历要提取的变量。使用 bash 正则表达式来提取相应变量的值。如果变量不存在,则将其设置为空。
  • (Off topic) 看到这些答案后,我觉得这个问题有资格迁移到PCG

标签: awk sed


【解决方案1】:

正确答案取决于您要如何处理变量。

假设您需要它们作为 shell 变量,这里有一个不同的方法

$ while IFS= read -r line; 
  do A=""; B=""; C=""; 
     source <(echo "$line" | grep -oP "(A|B|C)=\w*" ); 
     echo "A=$A B=$B C=$C"; 
  done < file

A=one B=two C=three
A= B=six C=
A=eight B=eleven C=

诀窍是使用source 用于从带有grep 的每一行中提取的变量声明。由于值分配会延续,因此您需要在每个新行之前重置它们。

【讨论】:

    【解决方案2】:

    如果perl 是您的选择,请尝试:

    perl -ne 'undef %a; while (/([\w]+)=([\w]*)/g) {$a{$1}=$2;}
        for ("A", "B", "C") {print "$_=\"$a{$_}\"\n";}' file_test
    

    输出:

    A="one"
    B="two"
    C="three"
    A=""
    B="six"
    C=""
    A="eight"
    B="eleven"
    C=""
    

    它解析每一行分配=,将键值对存储在一个关联数组%a中,然后最终报告A、B和C的值。

    【讨论】:

      【解决方案3】:

      我偏爱awk 解决方案,例如

      $ awk '{for (i = 1; i <= NF; i++) if ($i ~ /^[A-Za-z_][^=]*[=]/) print $i}' file
      A=one
      B=two
      C=three
      D=four
      A=
      B=six
      D=seven
      A=eight
      E=nine
      D=ten
      B=eleven
      

      说明

      • for (i = 1; i &lt;= NF; i++) 循环遍历每个空格分隔的字段;
      • if ($i ~ /^[A-Za-z_][^=]*[=]/) 如果字段以至少一个字符开头,即[A-Za-z_] 后跟'=';那么
      • print $i 打印该字段。

      【讨论】:

      • 为什么正则表达式匹配的下划线部分?我认为根据 OP 的规范,它应该只是 [ABC]=
      • 好吧,老实说,这是因为 shell 可以用它启动一个变量。 (或者至少那是我的思考过程)坚持使用[A-Za-z] 会很好,但我知道有人会说——嘿,你也可以使用'_' :) 但是,是的,我同意,定制它到输入 [A-Z] 将是所有需要的。
      • 好的,你想提取所有个可能的变量赋值。
      • 是的,我看到了,只是试了一下。这确实是跟踪连续字母的一种很酷的方法。取决于他需要如何使用它。如果有可能在文件包含上方定义了任何未命名的连续字母,然后在某个时候使用,我可能会看到一个小问题,但除此之外,我喜欢它并将它塞进我的工具箱。
      • 唯一的想法是假设他有一个脚本。无论出于何种原因,他都知道他正在解析的文件中的变量不会与他的脚本中已经存在的任何变量发生冲突。现在假设他在他的脚本顶部定义了一个B=something,并且在我们解析的数据文件中没有B,但是如果我们将B=作为一个连续的字母,并在那个时候将它设置为空'没想到,如果他后来使用脚本B 认为它仍然设置,我可以看到这会导致问题。我并不是说这很可能,而是当我想到它时,我脑海中突然出现了一个想法:)
      【解决方案4】:

      在我的前 3 个解决方案中,我正在考虑您需要使用来自字符串 A,B,C 的值的 shell 变量,并且您不想简单地打印它们,如果是这种情况,那么以下可能会对您有所帮助.



      第一个解决方案:它认为你的变量A,B,C总是在同一个字段编号。

      while read first second third fourth fifth sixth
      do
        echo $third,$fourth,$fifth        ##Printing values here.
        a_var=${third#*=}
        b_var=${fourth#*=}
        c_var=${fifth#*=}
        echo "Using new values of variables here...."
        echo "NEW A="$a_var
        echo "NEW B="$b_var
        echo "NEW C="$c_var
      done < "Input_file"
      

      它只是在每一行中打印变量值,因为您没有告诉您要对这些变量做什么用途,所以我只是打印它们,您也可以根据您的用例使用它们。



      第二个解决方案: 这认为变量的顺序相同,但它确实检查 A 是否排在第 3 位,B 是否排在第 4 位或不是等并相应地打印。

      while read first second third fourth fifth sixth
      do
        echo $third,$fourth,$fifth        ##Printing values here.
        a_var=$(echo "$third" | awk '$0 ~ /^A/{sub(/.*=/,"");print}')
        b_var=$(echo "$fourth" | awk '$0 ~ /^B/{sub(/.*=/,"");print}')
        c_var=$(echo "$fifth" | awk '$0 ~ /^C/{sub(/.*=/,"");print}')
        echo "Using new values of variables here...."
        echo "NEW A="$a_var
        echo "NEW B="$b_var
        echo "NEW C="$c_var
      done < "Input_file"
      


      第三种解决方案:看起来非常适合您的要求,不确定编码副的效率如何(如果我们也可以在这里做其他事情,我仍在分析更多)。此代码不会查找ABC 的行内顺序,它将匹配它让它们在行中的任何位置,如果找到匹配,它将分配变量的值,否则它将是 NULL 值.

      while read line
      do
        a_var=$(echo "$line" | awk 'match($0,/A=[^ ]*/){val=substr($0,RSTART,RLENGTH);sub(/.*=/,"",val);print val}')
        b_var=$(echo "$line" | awk 'match($0,/B=[^ ]*/){val=substr($0,RSTART,RLENGTH);sub(/.*=/,"",val);print val}')
        c_var=$(echo "$line" | awk 'match($0,/C=[^ ]*/){val=substr($0,RSTART,RLENGTH);sub(/.*=/,"",val);print val}')
        echo "Using new values of variables here...."
        echo "NEW A="$a_var
        echo "NEW B="$b_var
        echo "NEW C="$c_var
      done < "Input_file
      

      输出如下。

      Using new values of variables here....
      NEW A=one
      NEW B=two
      NEW C=three
      Using new values of variables here....
      NEW A=
      NEW B=six
      NEW C=
      Using new values of variables here....
      NEW A=eight
      NEW B=eleven
      NEW C=
      


      EDIT1:如果您只想打印 A,B,C 的值,请尝试以下操作。

      awk '{
       for(i=1;i<=NF;i++){
         if($i ~ /[ABCabc]=/){
           sub(/.*=/,"",$i)
           a[++count]=$i
         }
       }
       print "A="a[1] ORS "B=" a[2] ORS "C="a[3];count=""
       delete a
      }'  Input_file
      

      【讨论】:

        【解决方案5】:

        另一个 Perl

        perl -lne ' %x = /(\S+)=(\S+)/g ; for("A","B","C") { print "$_ = $x{$_}" } %x=() '
        

        输入文件

        $ perl -lne ' %x = /(\S+)=(\S+)/g ; for("A","B","C") { print "$_ = $x{$_}" } %x=() ' file_test
        A = one
        B = two
        C = three
        A =
        B = six
        C =
        A = eight
        B = eleven
        C =
        $
        

        【讨论】:

          【解决方案6】:

          一个通用变量 awk 已记录在案。 假设变量分隔符是=,而不是之前文本的一部分,也不是变量内容本身。

          awk 'BEGIN {
                  # load the list of variable and order to print
                  VarSize = split( "A B C", aIdx )
                  # create a pattern filter for variable catch in lines
                  for ( Idx in aIdx ) VarEntry = ( VarEntry ? ( VarEntry "|^" ) : "^" ) aIdx[Idx] "="
                  }
          
                  {
                  # reset varaible value
                  split( "", aVar )
                  # for each part of the line
                  for ( Fld=1; Fld<=NF; Fld++ ) {
                     # if part is a varaible assignation
                     if( $Fld ~ VarEntry ) {
                        # separate variable name and content in array
                        split( $Fld, aTemp, /=/ )
                        # put variable content in corresponding varaible name container
                        aVar[aTemp[1]] = aTemp[2]
                        }
                     }
                  # print all variable content (empty or not) found on this line
                  for ( Idx in aIdx ) printf( "%s = \042%s\042\n", aIdx[Idx], aVar[aIdx[Idx]] )
                  }
                ' YourFile
          

          【讨论】:

            【解决方案7】:

            不清楚您是在尝试设置 awk 变量还是 shell 变量,但这里是如何填充关联 awk 数组,然后使用它来填充关联 shell 数组:

            $ cat tst.awk
            BEGIN {
                numKeys = split("A B C",keys)
            }
            {
                delete f
                for (i=1; i<=NF; i++) {
                    if ( split($i,t,/=/) == 2 ) {
                        f[t[1]] = t[2]
                    }
                }
                for (keyNr=1; keyNr<=numKeys; keyNr++) {
                    key = keys[keyNr]
                    printf "[%s]=\"%s\"%s", key, f[key], (keyNr<numKeys ? OFS : ORS)
                }
            }
            
            $ awk -f tst.awk file
            [A]="one" [B]="two" [C]="three"
            [A]="" [B]="six" [C]=""
            [A]="eight" [B]="eleven" [C]=""
            
            $  while IFS= read -r out; do declare -A arr="( $out )"; declare -p arr; done < <(awk -f tst.awk file)
            declare -A arr=([A]="one" [B]="two" [C]="three" )
            declare -A arr=([A]="" [B]="six" [C]="" )
            declare -A arr=([A]="eight" [B]="eleven" [C]="" )
            
            $ echo "${arr["A"]}"
            eight
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-17
              • 2022-10-17
              • 1970-01-01
              • 2016-09-26
              相关资源
              最近更新 更多