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가 겹치는 것을 처리할 수 없습니다.
다음은 겹침을 방지하기 위해 a 를 사용하고 입력 줄에 키가 이미 존재하는 경우 명시적 테스트를 제거하기 위해 setdefault 를 사용하여 문자에 대한 의존도를 줄이고 명시 set()
적 테스트를 제거함으로써 문제를 올바르게 처리합니다 .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