簡體   English   中英

Bash使用不規則的行參數遍歷文件

[英]Bash iterating over file with irregular line arguments

我有一些從.csv格式格式化的不規則.txt文件。 文件包含以下數據,以分號分隔:

A;B;C;D;E;F;G;H;
A;B;C;D;E;F;G;H;I;J;K;
A;B;C;D;E;F;G;H;I;J;K;L;M;N;
A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;

我想做的是從每一行中獲取特定的值。 我使用的代碼示例如下所示,並且當包含相同編號的行時效果很好。 分隔符:

OIFS=$IFS
IFS=";"
while read var1 var2 var3 var4 var5 var6 var7 var8 var9 var10
do
echo $var2, $var6, $var7, $var8
done < test.txt
IFS=$OIFS

但是我對代碼的實現一竅不通,因為代碼的實現不算什么。 的“;” 並采取具體行動。 應考慮每一行的“ B”列以及“ E”列之后的所有內容。 最小“;”號 在每行中為8,最大值為20(以“ 3”遞增)。 所需的輸出是:

對於包含8“;”的行

echo $B { $F { $G:$H } }

對於包含11“;”的行

echo $B { $F { $G:$H } $I { $J:$K } }

對於帶有14“;”的行

echo $B { $F { $G:$H } $I { $J:$K } $L { $M:$N } }

等等。 在bash中可行嗎?
謝謝。

我不確定我是否完全了解您想要做什么,但這可能會作為第一步。

應考慮每一行的“ B”列以及“ E”列之后的所有內容。

為此,您可以使用cut命令:

cut -d ';' -f 2,6-

其中-d ';' 設置定界符,然后-f 2,6-選擇字段2和6。

此后將選擇列$B和列$F

您還可以使用--output-delimiter更改輸出--output-delimiter

讀取每一行成使用一個數組-a選項來read ; 這使得處理變長線變得更加容易。

while IFS=';' read -a vars; do
    printf "%s {" "${vars[1]}"
    for ((i=5; i<${#vars[@]}; i+=3)); do
        printf " %s { %s %s }" "${vars[@]:i:3}"
    done
    printf " }\n"
done < test.txt

另外,您可以使用python做您想要做的事情(如果我正確理解的話):

import fileinput

# http://stackoverflow.com/questions/34576772/bash-iterating-over-file-with-irregular-line-arguments/34576899#34576899

def columns_are_valid(columns):
    return len(columns) >= 8 and len(columns) % 3 == 2

# Returns every three columns as a tuple
# Example: 1,2,3,4,4,5,6,7,8,9  ->  (1,2,3) , (4,5,6) , (7,8,9)
def every_three(rest_columns):
    it = iter(rest_columns)
    while True:
        yield next(it), next(it), next(it)


for line in fileinput.input():
    line = line.rstrip(';\n')  # remove trailing newline and ';'
    columns = line.split(';') # split by ';'
    assert columns_are_valid(columns)

    column_b = columns[1]

    # Selects columns F onwards
    columns_f_onwards = columns[5:]

    # Format parts like '$F { $G:$H }'
    parts = [ '%s {%s:%s}' % (a,b,c) for a,b,c in every_three(columns_f_onwards) ]
    space_delimited_parts = ' '.join(parts)

    print '{ %s { %s }' % (column_b, space_delimited_parts)

示例運行:

 % python myscript.py

有輸入:

A;B;C;D;E;F;G;H;
A;B;C;D;E;F;G;H;I;J;K;
A;B;C;D;E;F;G;H;I;J;K;L;M;N;
A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;

輸出:

{ B { F {G:H} }
{ B { F {G:H} I {J:K} }
{ B { F {G:H} I {J:K} L {M:N} }
{ B { F {G:H} I {J:K} L {M:N} O {P:Q} }

僅Bash解決方案:

#!/bin/bash

OLD_IFS=$IFS
IFS=";"
while read line; do
    set -- $line
    echo -n "$2 { "
    shift 5
    while [[ -n $1 ]];do
        echo -n "$1 { $2:$3 } "
        shift 3
    done
    echo "}"
done < data
IFS=$OLD_IFS

輸入文件:

$ cat data 
A;B;C;D;E;F;G;H;
A;B;C;D;E;F;G;H;I;J;K;
A;B;C;D;E;F;G;H;I;J;K;L;M;N;
A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;

結果:

$ ./script.sh 
B { F { G:H } }
B { F { G:H } I { J:K } }
B { F { G:H } I { J:K } L { M:N } }
B { F { G:H } I { J:K } L { M:N } O { P:Q } }

解決方案2

相同但有數組

#!/bin/bash

OLD_IFS=$IFS
IFS=";"
os=5
while read line;do
    c=0
    a=($line)
    echo -n "${a[1]} { "
    while [[ -n ${a[$((os+c*3))]} ]];do
        echo -n "${a[$((os+c*3))]} { "
        echo -n "${a[$((os+c*3+1))]}:${a[$((os+c*3+2))]} } "
        ((c++))
    done
    echo "}"
done < data
IFS=$OLD_IFS

我認為您到目前為止表現良好! 您只需要一些小提示:

  • 您可以為一個命令設置一個shell變量
    稍微改變了IFS的解決方案。
  • 您可以檢查restg var,看看是否為空
  • 我將在變量中使用${x}
    此代碼不是必需的,而是一個好習慣。
  • 使用read -r不是簡單的read

下一個代碼是當您知道字段數量很少時的操作方法。 現在您最多有20個字段,因此可以在第一個解決方案中添加更多的var和代碼:

while IFS=";" read -r var1 var2 var3 var4 var5 var6 var7 var8 var9 var10 var11 var12 var13 var14; do
      echo $var2, $var6, $var7, $var8
      if [ -z "${var9}" ]; then
         echo "Line without 8 delimiters"
      elif [ -z "${var10}${var11}${var12}" ]; then
         echo "Line with 9 delimiters"
      else
         echo "Line with more than 9 delimiters"
      fi   
 done

我沒有完成上面的代碼,因為它的結構不好。
您想用一個照顧重復組的功能來實現這一點。

function repeatgroup {
   output=""
   remaining="$*"
   printf "{ "
   while [ -n "${remaining}" ]; do
       rem1=$(echo "$remaining" | cut -d";" -f1)
       rem2=$(echo "$remaining" | cut -d";" -f2)
       rem3=$(echo "$remaining" | cut -d";" -f3)
       remaining=$(echo "$remaining" | cut -d";" -f4-)
       printf "%s {%s:%s} " "${rem1}" "${rem2}" "${rem3}"
   done
}

    while IFS=";" read -r var1 var2 var3 var4 var5 remaining; do
          if [ -z "${var5}${remaining}" ]; then
             echo "field shortage"
          elif [ -z "${remaining}" ]; then
             echo "Line without 8 delimiters"
             echo "{ ${var2} }"
          else
             printf "{ %s " "${var2}"
             repeatgroup "${remaining}"
             printf "}\n"
          fi
     done < input

備注:
可以使用內部Bash函數編寫rem1=$(echo "$remaining" | cut -d";" -f1)remaining=$(echo "$remaining" | cut -d";" -f4-) ,但是我認為代碼將變得難以理解。 當您需要解析大文件時,可以先嘗試。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM