读取文件并根据行中的内容提取变量

时间:2019-02-08 03:03:44

标签: awk sed

我有一个看起来像这样的文件:

$ 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将行划分为多个字段,并根据每个字段设置变量,但是我不知道从哪里开始或如何开始。

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

7 个答案:

答案 0 :(得分:1)

正确答案取决于您要对变量进行什么处理。

假设您需要将它们作为外壳变量,这是另一种方法

$ 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从每一行中提取变量声明。由于值分配会继续执行,因此您需要在每行新行之前重置它们。

答案 1 :(得分:0)

在我的前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是否排在第三位,B是否排在第四位或不等,并进行相应打印。

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的顺序,它将匹配它,让它们在行中的任何地方,如果找到匹配,它将分配变量OR的值否则它将为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

答案 2 :(得分:0)

如果您选择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=""

它使用=解析每一行分配,将键值对存储在assoc数组%a中,最后报告A,B和C的值。

答案 3 :(得分:0)

我偏爱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 <= NF; i++)在每个用空格分隔的字段上循环;
  • if ($i ~ /^[A-Za-z_][^=]*[=]/),如果该字段以至少一个字符[A-Za-z_]开头,后跟'=';然后
  • print $i打印字段。

答案 4 :(得分:0)

另一个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 =
$

答案 5 :(得分:0)

记录的通用变量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

答案 6 :(得分:0)

尚不清楚您是要设置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