在bash中处理定界线

时间:2019-09-20 02:00:18

标签: bash shell parsing awk

给输入的单行带有'n'参数,它们之间用空格分隔。输入参数本身是可变的。输入是通过外部文件给出的。

我想根据正则表达式将特定元素移至变量。因此,我正在考虑先声明一个指针变量,以跟踪我所在的行。另外,变量的分配与数字顺序无关,并且取决于输入,某些变量可能会被完全跳过。

我目前的方法是使用 awk '{print $1}' file.txt 但是,并非所有元素都是固定的,因此我需要考虑可能不存在或可能具有多个条目的元素。

更新:我找到了另一种方法。

file=$(cat /file.txt)
for i in ${file[@]}; do 
   echo $i >> split.txt; 
done

通过这种方式,我们得到了带有单个参数的多行,而不是带有多个参数的单行。这样,我们现在可以使用var#=(grep --regexp="[pattern]" split.txt。现在,我只需要弄清楚如何最好地使用正则表达式来过滤这种混乱情况。

让我举个例子。

我的输入字符串是:

RON KKND 1534Z AUTO 253985G 034SRT 134OVC 04/32

RON KKND 5256Z 143623G72K 034OVC 074OVC 134SRT 145PRT 13/00

RON KKND 2234Z CON 342523G CLS 01/M12 RMK

因此,上述每个变量的赋值为:

var1=RON var2=KKND var3=1534Z var4=TRUE var5=FALSE var6=253985G varC=2 varC1=034SRT varC2=134OVC var7=04/32

var1=RON var2=KKND var3=5256Z var4=FALSE var5=FALSE var6=143623G72K varC=4 varC1=034OVC varC2=074OVC varC3=134SRT varC4=145PRT var7=13/00

var1=RON var2=KKND var3=2234Z var4=FALSE var5=TRUE var6=342523G varC=0  var7=01/M12

因此,第四个参数可能是var4,var5或var6。 第五个参数可以是var5,var6或与其他条件匹配。 第六个参数可以是var6,也可以不是。可以通过将每个参数与*/*

匹配来确定var6和var7之间

将其归结为更多,var1,var2和var3的输入位置是固定的,但此后我需要比较,排序和分配。此外,参数本身的字符长度可以不同。要分割的每个部分的相对位置相对于其相邻部分是固定的。例如,在输入中var7绝不会在var6之前,如果var4和var5为true,则第4个和第5个自变量始终为'AUTO CON'。某些段始终为一个自变量,而其他段则始终为一个以上。每个的相对位置是已知的。对于每种模式,某些模式在特定位置具有特定字符,而其他模式除了在序列中的位置之外,可能没有任何标志。

所以我需要awk来识别一个指针变量,因为需要检查每个参数,直到找到特定的匹配项为止

#Check to see if var4 or var5 exists. if so, flag and increment pointer
pointer=4
if (awk '{print $$pointer}' file.txt) == "AUTO" ; then
   var4="TRUE"
   pointer=$pointer+1
else
   var4="FALSE"
fi
if (awk '{print $$pointer}' file.txt) == "CON" ; then
   var5="TRUE"
   pointer=$pointer+1
else
   var5="FALSE"
fi

#position of var6 is fixed once var4 and var5 are determined
var6=$(awk '{print $$pointer}' file.txt)
pointer=$pointer+1

#Count the arguments between var6 and var7 (there may be up to ten)
#and separate each to decode later. varC[0-9] is always three upcase 
# letters followed by three numbers. Use this counter later when decoding.
varC=0

until (awk '{print $$pointer}' file.txt) == "*/*" ; do

   varC($varC+1)=(awk '{print $$pointer}' file.txt)
   varC=$varC+1
   pointer=$pointer+1
done
#position of var7 is fixed after all arguments of varC are handled
var7=$(awk '{print $$pointer}' file.txt)
pointer=$pointer+1

我知道以上语法不正确。问题是我该如何解决。

var7并不总是在输入行的末尾。但是,无需处理var7之后的参数。

实际上是解释我还没有想到的模式。我打算使用用case语句比较变量和正则表达式来进行处理。我不想使用awk直接解释模式,因为那样会很混乱。我已经考虑过使用for n in $string,但是这样做意味着直接将每个参数与每个可能的组合进行比较(并且有多个段,每个段都有多个模式),这是不切实际的。我正在尝试将其分为两个步骤。

2 个答案:

答案 0 :(得分:0)

已更新:此代码显示了如何多次基于模式匹配确定变量值。
一个代码块以纯bash格式,另一个以gawk方式

bash代码块需要关联数组支持,在早期版本中不可用
还需要grep进行模式匹配
经过GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)grep (GNU grep) 2.20测试 并在我学会了why-is-printf-better-than-echo后坚持学习printf,而不是echo
在使用bash时,我认为最好的做法是提高防御力

#!/bin/bash
declare -ga outVars
declare -ga lineBuf
declare -g NF
#force valid index starts from 1
#consistent with var* name pattern
outVars=(unused var1 var2 var3 var4 var5 var6 varC var7)
((numVars=${#outVars[@]} - 1))
declare -gr numVars
declare -r outVars

function e_unused {
    return
}
function e_var1 {
    printf "%s"  "${lineBuf[1]}"
}
function e_var2 {
    printf "%s"  "${lineBuf[2]}"
}
function e_var3 {
    printf "%s"  "${lineBuf[3]}"
}

function e_var4 {
    if [ "${lineBuf[4]}" == "AUTO" ] ;
    then
        printf "TRUE"
    else
        printf "FALSE"
    fi
}
function e_var5 {
    if [ "${lineBuf[4]}" == "CON" ] ;
    then
        printf "TRUE"
    else
        printf "FALSE"
    fi
}
function e_varC {
    local var6_idx=4
    if [ "${lineBuf[4]}" == "AUTO" -o "${lineBuf[4]}" == "CON" ] ;
        then
            var6_idx=5
    fi

    local var7_idx=$NF
    local i
    local count=0
    for ((i=NF;i>=1;i--));
    do
        if [ $(grep -cE '^.*/.*$' <<<${lineBuf[$i]}) -eq 1 ];
            then
            var7_idx=$i
            break
        fi
    done
    ((varC = var7_idx - var6_idx - 1))
    if [ $varC -eq 0 ];
        then
        printf 0
        return;
    fi
    local cFamily=""
    local append
    for ((i=var6_idx;i<=var7_idx;i++));
    do
        if [ $(grep -cE '^[0-9]{3}[A-Z]{3}$' <<<${lineBuf[$i]}) -eq 1 ];
            then
            ((count++))
            cFamily="$cFamily varC$count=${lineBuf[$i]}"
        fi
    done
    printf "%s %s"  $count "$cFamily"
}

function e_var6 {
    if [ "${lineBuf[4]}" == "AUTO" -o "${lineBuf[4]}" == "CON" ] ;
        then
        printf "%s"  "${lineBuf[5]}"
    else
        printf "%s"  "${lineBuf[4]}"
    fi
}
function e_var7 {
    local i
    for ((i=NF;i>=1;i--));
    do
        if [ $(grep -cE '^.*/.*$' <<<${lineBuf[$i]}) -eq 1 ];
            then
            printf "%s"  "${lineBuf[$i]}"
            return
        fi
    done
}

while read  -a lineBuf ;
    do
    NF=${#lineBuf[@]}
    lineBuf=(unused ${lineBuf[@]})
    for ((i=1; i<=numVars; i++));
        do
        printf "%s="  "${outVars[$i]}"
        (e_${outVars[$i]})
        printf " "
    done
    printf "\n"

done <file.txt

下面的awk代码中使用了gawk特定的扩展名Indirect Function Call
该代码为每个所需的输出变量分配一个函数名称。
可以在其特定功能中应用不同的模式或其他转换
这样做是为了避免大量if-else-if-else
并且更易于阅读和扩展。
对于特殊的varC系列,函数pick_varC发挥了作用
确定varC之后,其值包含多个输出字段。
如果varC=2,则varC的值返回为2 varC1=034SRT varC2=134OVC
这是varC附加所有跟随成员的实际值。

gawk '
    BEGIN {
        keys["var1"] = "pick_var1";
        keys["var2"] = "pick_var2";
        keys["var3"] = "pick_var3";
        keys["var4"] = "pick_var4";
        keys["var5"] = "pick_var5";
        keys["var6"] = "pick_var6";
        keys["varC"] = "pick_varC";
        keys["var7"] = "pick_var7";
    }

    function pick_var1 () {
        return $1;
    }
    function pick_var2 () {
        return $2;
    }
    function pick_var3 () {
        return $3;
    }

    function pick_var4 () {
        for (i=1;i<=NF;i++) {
            if ($i == "AUTO") {
                return "TRUE";
            }
        }
        return "FALSE";
    }

    function pick_var5 () {
        for (i=1;i<=NF;i++) {
            if ($i == "CON") {
                return "TRUE";
            }
        }
        return "FALSE";
    }

    function pick_varC () {
        for (i=1;i<=NF;i++) {
            if (($i=="AUTO" || $i=="CON")) {
                break;
            }
        }
        var6_idx = 5;
        if ( i!=4 ) {
            var6_idx = 4;
        }
        var7_idx = NF;
        for (i=1;i<=NF;i++) {
            if ($i~/.*\/.*/) {
                var7_idx = i;
            }
        }
        varC = var7_idx - var6_idx - 1;
        if ( varC == 0) {
            return varC;
        }
        count = 0;
        cFamily = "";
        for (i = 1; i<=varC;i++) {
            if ($(var6_idx+i)~/[0-9]{3}[A-Z]{3}/) {
                cFamily = sprintf("%s varC%d=%s",cFamily,i,$(var6_idx+i));
                count++;
            }
        }
        varC = sprintf("%d %s",count,cFamily);
        return varC;
    }

    function pick_var6 () {
        for (i=1;i<=NF;i++) {
            if (($i=="AUTO" || $i=="CON")) {
                break;
            }
        }
        if ( i!=4 ) {
            return $4;
        } else {
            return $5
        }
    }

    function pick_var7 () {
        for (i=1;i<=NF;i++) {
            if ($i~/.*\/.*/) {
                return $i;
            }
        }
    }

    {
        for (k in keys) {
            pickFunc = keys[k];
            printf("%s=%s ",k,@pickFunc());
        }
        printf("\n");
    }
    ' file.txt

测试输入

RON KKND 1534Z AUTO 253985G 034SRT 134OVC 04/32
RON KKND 5256Z 143623G72K 034OVC 074OVC 134SRT 145PRT 13/00
RON KKND 2234Z CON 342523G CLS 01/M12 RMK

脚本输出

var1=RON var2=KKND var3=1534Z var4=TRUE var5=FALSE varC=2  varC1=034SRT varC2=134OVC var6=253985G var7=04/32
var1=RON var2=KKND var3=5256Z var4=FALSE var5=FALSE varC=4  varC1=034OVC varC2=074OVC varC3=134SRT varC4=145PRT var6=143623G72K var7=13/00
var1=RON var2=KKND var3=2234Z var4=FALSE var5=TRUE varC=0  var6=342523G var7=01/M12

答案 1 :(得分:0)

请尝试以下操作:

#!/bin/bash

# template for variable names
declare -a namelist1=( "var1" "var2" "var3" "var4" "var5" "var6" "varC" )
declare -a ary

# read each line and assign ary to the elements
while read -r -a ary; do
    if [[ ${ary[3]} = AUTO ]]; then
        ary=( "${ary[@]:0:3}" "TRUE" "FALSE" "${ary[4]}" "" "${ary[@]:5:3}" )
    elif [[ ${ary[3]} = CON ]]; then
        ary=( "${ary[@]:0:3}" "FALSE" "TRUE" "${ary[4]}" "" "${ary[@]:5:3}" )
    else
        ary=( "${ary[@]:0:3}" "FALSE" "FALSE" "${ary[3]}" "" "${ary[@]:4:5}" )
    fi
    # initial character of the 7th element
    ary[6]=${ary[7]:0:1}

    # locate the index of */* entry in the ary and adjust the variable names
    for (( i=0; i<${#ary[@]}; i++ )); do
        if [[ ${ary[$i]} == */* ]]; then
            declare -a namelist=( "${namelist1[@]}" )
            for (( j=1; j<=i-7; j++ )); do
                namelist+=( "$(printf "varC%d" "$j")" )
            done
            namelist+=( "var7" )
        fi
    done

    # assign variables to array elements
    for (( i=0; i<${#ary[@]}; i++ )); do
#       echo -n "${namelist[$i]}=${ary[$i]} "       # for debugging
        declare -n p="${namelist[$i]}"
        p="${ary[$i]}"
    done
#   echo "var1=$var1 var2=$var2 var3=$var3 ..."     # for debugging
done < file.txt

请注意,上面的脚本仅分配bash变量,不打印任何内容 除非您明确地echoprintf变量。