혼자서 해결하려고 노력했지만 성공하지 못한 문제에 대해 도움을 요청합니다. 곧 다음과 같은 구조를 가진 매우 큰 테이블 형식 데이터 파일을 처리해야 했습니다.
14 R
16 I
21 B
22 C
23 Q
24 E
33 R
34 L
41 K
62 F
63 F
64 M
88 B
잠깐만요... 이 정렬된 오름차순 데이터로 하려는 작업은 첫 번째 열의 세 개 이상의 연속 용어 블록에 해당하는 두 번째 열의 항목을 정렬하는 것입니다. 따라서 위 데이터의 예상 출력은 다음과 같아야 합니다.
21-24 BCQE
82-64 FFM
지금까지 내가 완성한 코드는 다음과 같습니다.
prev=0
val=$(prev + 1)
while read -r n a ; do
if [[ ${n} == ${val} ]]
t="$( "$a" + ( "$(a - 1)" ) )" ; then
echo "$t"
fi
prev=$n
done < table
하지만 작동하지 않습니다.
답변1
해결책 awk
:
awk '{if(p+1==$1){c+=1}else{ if(c>1){printf "%s-%s %s\n", b, p, s;} c=0;s=""}} c==1{b=p} {p=$1;s=s$2}' file
이번에는 설명이 더 읽기 쉽습니다.
awk '{
if(p+1==$1){
c+=1 # increment the counter if the value is consecutive
} else {
if(c>1){
# print the begin and end values with the concatenated string
printf "%s-%s %s\n", b, p, s;
}
c=0 # reset the counter
s="" # reset the string to print
}
}
c==1{b=p} # set the begin value
{p=$1;s=s$2} # set the previous variable and the string for the next loop
' file
GNU를 사용하여 테스트 awk
하고mawk
답변2
사용 awk
:
$ awk 'function out() { if (start != "") { if (start == prev) printf("%s\t%s\n", prev, string); else printf("%s-%s\t%s\n", start, prev, string) } } $1 != prev + 1 { out(); start = $1; string = "" } { prev = $1; string = string $2 } END { out() }' file
14 R
16 I
21-24 BCQE
33-34 RL
41 K
62-64 FFM
88 B
프로그램 awk
:
function out() {
if (start != "") {
if (start == prev)
printf("%s\t%s\n", prev, string)
else
printf("%s-%s\t%s\n", start, prev, string)
}
}
$1 != prev + 1 { out(); start = $1; string = "" }
{ prev = $1; string = string $2 }
END { out() }
프로그램은 의 첫 번째 열에 있는 이전 숫자 prev
와 에 있는 두 번째 열의 연결을 추적합니다 string
. 이전 첫 번째 열이 현재 첫 번째 열보다 1 적으면 발생하는 모든 일은 업데이트 prev
됩니다 string
.
넘버링에 "공백"이 있는 경우, out()
수집된 데이터를 기록된 간격과 함께 출력하기 위해 호출됩니다 . 이 함수는 입력이 끝날 때 호출되기도 합니다.
쉘의 축어적 동등물은 다음과 같습니다 sh
:
out () {
if [ -n "$start" ]; then
if [ "$start" = "$prev" ]; then
printf '%s\t%s\n' "$prev" "$string"
else
printf '%s-%s\t%s\n' "$start" "$prev" "$string"
fi
fi
}
while read -r num str; do
if [ "$num" -ne "$(( prev + 1 ))" ]; then
out
start=$num
string=""
fi
prev=$num
string=$string$str
done <file
out
방금 숫자상으로 서로 이어지는 행이 두 개만 있어도 이것이 결합된다는 것을 알았습니다. 나중에 수정할 수도 있지만 지금은 여기에 그대로 두겠습니다.
답변3
다른 곳에서 언급했듯이 bash는 작업에 가장 적합한 도구가 아닐 수 있으며 Perl이나 awk에서 수행하는 것이 더 쉬울 수 있습니다. 이것조차:
#! /bin/bash
print() {
# "${array[*]}" joins the elements with the first characters of IFS as separator
# so we set IFS to the empty string so that the elements are simply concatenated
local IFS=
if (( end - start > 1 )) # more than two consecutive numbers, concatenate
then
printf "%s-%s\t%s\n" "$start" "$end" "${chars[*]}"
elif (( start == end )) # single number, nothing special
then
printf "%s\t%s\n" "$start" "${chars[0]}"
elif (( end - start == 1 )) # two consecutive numbers, print separately
then
printf "%s\t%s\n" "$start" "${chars[0]}" "$end" "${chars[1]}"
fi
}
# An initial read
read -r n a
chars=( "$a" )
start=$n
end=$n
while read -r n a
do
if (( n - end == 1 )) # consecutive numbers, store for printing
then
chars+=( "$a" )
end=$n
continue # move to next line
fi
print # Break in numbers, print stored set
chars=( "$a" ) # reset variables
start=$n
end=$n
done
print # print last set
다른 줄이 필요하지 않으면 함수 elif
에서 블록을 제거할 수 있습니다 print
.
출력 예:
14 R
16 I
21-24 BCQE
33 R
34 L
41 K
62-64 FFM
88 B