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