是否有任何干净的方法使用一些 bash 魔法来计算文本文件第二列的元素之间的范围? (我目前正在使用 Python 执行此操作)。
输入:文件1
A 1-5
A 17-19
B 1-5
B 4-6
预期输出:文件 2
A 1,2,3,4,5,17,18,19
B 1,2,3,4,5,6
编辑@Anthon:为了累积元素,我正在使用类似的东西(然后使用 for 循环计算范围)
d_pos= {}
for row in open('File.txt'):
x, y = [ value.strip() for value in row.split('\t')]
if x in d_pos:
d_pos[x].append(y)
else:
d_pos[x] = [y]
答案1
直接 bash,既然你问了(尽管注意我使用的是关联数组,需要 bash 4.0)。
诀窍在于大括号序列扩展表达式{x..y}
,对于整数 x ,任何 y 都会作为文本列表扩展到包含的值范围(即 [x,y])。我们也需要添加一个eval
,因为大括号扩展发生在变量扩展之前。
declare -A data seen # explicit associative arrays
while read col range; do
data[$col]="${data[$col]} $(eval echo {${range/-/..}})"
done <<DATA
A 1-5
A 17-19
B 1-5
B 4-6
DATA
# dump array
#declare -p data
for ii in ${!data[@]}; do
seen=(); datum=""
# build list of unique values
for dd in ${data[$ii]}; do
(( ${seen[$dd]:-0} )) || datum="$datum $dd"
let seen[$dd]++
done
datum=${datum# } # drop leading space
datum=${datum// /,} # spaces to commas
printf "%-4s %s\n" "$ii" "$datum"
done
序列扩展的一个变体是a{x..y}b
在扩展的每个项前面添加“a”,并附加“b”:您可以使用它来附加一个“,”,并根据需要更改数据变量。序列扩展处理增量 1,如果 x > y,则处理 -1
您可能还需要对输出进行排序:迭代关联数组的键没有明确定义的顺序,并且您没有说明输入范围是否已预先排序(因此我没有使代码过于复杂)。
答案2
您的 Python 代码很接近,但例如无法处理项目 B 的 4 和 5 的重叠。
以下正确处理该问题,使用 aset()
来防止重叠,使用 setdefault 来消除显式测试(如果键已存在于输入行中d_pos
和.split()
输入行上),以减少对\t
字符的依赖并消除显式测试.strip()
:
d_pos= {}
for row in open('File.txt'):
x, y = [ value for value in row.split()]
y1, y2 = map(int, y.split('-'))
d_pos.setdefault(x, set()).update(range (y1, y2+1))
for x in sorted(d_pos):
print '{}\t{}'.format(x, ','.join(map(str, d_pos[x])))
答案3
如果你可以使用perl
:
$ perl -MList::MoreUtils=uniq -anle '
($s,$e) = split "-", $F[1];
push @{$h{$F[0]}}, $s..$e;
END {
$" = ",";
print "$_ @{[uniq@{$h{$_}}]}" for keys %h;
}
' file
A 1,2,3,4,5,17,18,19
B 1,2,3,4,5,6
如果您不想使用List::MoreUtils
,因为当它不在核心中时,您可以执行以下操作:
$ perl -anle '
($s,$e) = split "-", $F[1];
push @{$h{$F[0]}}, $s..$e;
END {
$" = ",";
for $k (keys %h) {
%u=();
print "$k @{[grep {!$u{$_}++} @{$h{$k}}]}";
}
}
' file