Bash在具有不规则行参数的文件上迭代

Bash在具有不规则行参数的文件上迭代,bash,csv,Bash,Csv,我有许多不规则的.txt文件,格式是.csv文件。 文件包含以下以分号分隔的数据: 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 va

我有许多不规则的.txt文件,格式是.csv文件。 文件包含以下以分号分隔的数据:

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
对于包含11”的行

对于带有14”的行

等等。 在bash中是否可行?

谢谢。

我不确定我是否完全理解您想要做什么,但这可能是第一步

应考虑每行的“B”列以及“E”列之后存在的任何内容

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

cut -d ';' -f 2,6-
其中
-d';'
设置分隔符,并
-f2,6-
选择字段2和字段6

这将选择列
$B
和列
$F


您还可以使用
--output delimiter

更改输出的分隔符。或者,您可以使用python执行您想要的操作(如果我理解正确):

运行示例:

 % 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
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
结果:

$ ./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
#/bin/bash
OLD_IFS=$IFS
IFS=“;”
os=5
读行时;做
c=0
a=(美元行)
echo-n“${a[1]}{”
而[[-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++)
完成
回声“}”
完成<数据
IFS=$OLD_IFS

我认为到目前为止你做得很好!您只需要一些小提示:

  • 您可以为一个命令设置shell变量
    A稍微改变了IFS的解决方案
  • 您可以检查remaing vars并查看其是否为空
  • 我将在vars中使用
    ${x}

    这段代码不需要,但需要一个好习惯
  • 使用
    read-r
    而不是简单的
    read
下一个代码是当您知道您有少量字段时如何执行。现在最多有20个字段,因此可以向第一个解决方案添加更多变量和代码:

#!/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
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
函数组{
output=“”
剩余=“$*”
printf“{”
而[-n“${remaining}”];do
rem1=$(回显“$剩余”|切割-d”;“-f1)
rem2=$(回显“$剩余”|切割-d”;“-f2)
rem3=$(回显“$剩余”|切割-d”;“-f3)
剩余=$(回显“$剩余”|切割-d”“-f4-)
printf“%s{%s:%s}”“${rem1}”“${rem2}”“${rem3}”
完成
}
当IFS=“;”读取-r var1 var2 var3 var4 var5剩余时;执行
如果[-z“${var5}${remaining}”];则
回声“磁场不足”
elif[-z“${remaining}”];然后
回显“不带8个分隔符的行”
回显“{${var2}}”
其他的
printf“{%s”“${var2}”
重复组“${剩余}”
printf“}\n”
fi
完成<输入
备注:

rem1=$(echo“$resisting”| cut-d”;“-f1)
resisting=$(echo“$resisting”| cut-d”;“-f4-)
可以使用内部Bash函数编写,但我认为代码会变得很难理解。当需要解析大型文件时,可以先尝试。

使用
-a
选项将每行读取到数组中,以
读取
;这使得处理可变长度的行变得更加容易

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
当IFS=';“read-a vars;do
printf“%s{”“${vars[1]}”

for((i=5;iAbove命令已成功解析ouptut,只返回感兴趣的值。但是,这并没有解决问题。所需的输出应如下所示:如果给定行包含例如4列,则执行echo echo$A{$B{$C:$D}如果给定行有7列,则执行echo$A{$B{$C:$D}$E{$F:$G}对于10列,执行echo$A{$B{$C:$D}$E{$F:$G}$H{$I:$J}等等。因此脚本应该检查文件每行中的列数(由“;”分隔),并返回所需的输出。这是真的,请参阅我的另一个答案或@navious的答案:)这就像一个符咒。了解我们可以检查remaing vars并查看其是否为空是解决此问题的关键。非常感谢!我一直在等待Martin接受我的答案,因为我试图扩展他的想法。你的答案显然更好。我过去经常避免awk和数组,但这是一个简短的、强有力的演示f.向上投票。
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
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