我有一些从.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
但我仍然坚持使用代码的实现来计算否。 &#34;;&#34;并采取具体行动。 每一行的专栏&#34; B&#34;专栏和#34; E&#34;应该考虑在内。最小数量为&#34;;&#34;在每一行是8,而最大值是20(增加&#34; 3&#34;)。 期望的输出是:
对于包含8&#34;;&#34;
的行echo $B { $F { $G:$H } }
对于包括11&#34;;&#34;
的行echo $B { $F { $G:$H } $I { $J:$K } }
对于14&#34 ;;&#34;
的行echo $B { $F { $G:$H } $I { $J:$K } $L { $M:$N } }
等等。
它在bash中可行吗?
谢谢。
答案 0 :(得分:1)
我不确定我是否完全明白你想做什么,但这可能有助于作为第一步。
每一行的专栏&#34; B&#34;专栏和#34; E&#34;应该考虑在内。
为此,您可以使用cut
命令:
cut -d ';' -f 2,6-
-d ';'
设置分隔符,-f 2,6-
选择字段2和6以后。
这将从之后选择列$B
和列$F
。
您还可以使用--output-delimiter
答案 1 :(得分:1)
使用-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
答案 2 :(得分:0)
或者,您可以使用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} }
答案 3 :(得分:0)
仅限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
答案 4 :(得分:0)
我认为你到目前为止表现不错!你只需要一些小提示:
${x}
这段代码不需要,但这是一个好习惯。read -r
而非简单read
。下一个代码是当你知道你有少量字段时你可以做的。您现在最多有20个字段,因此您可以向第一个解决方案添加更多变量和代码:
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
注:
rem1=$(echo "$remaining" | cut -d";" -f1)
和remaining=$(echo "$remaining" | cut -d";" -f4-)
都可以使用内部Bash函数编写,但我认为代码很难理解。当您需要解析大文件时,可以先尝试。