다음과 같이 쉼표로 구분된 숫자 문자열이 있습니다.
1,2,3,5,6,7,8,9,12,14
bash
다음과 같이 인접한 숫자를 범위/하이픈 항목으로 결합하는 스크립트에서 사용할 명령을 찾고 있습니다 .
1-3,5-9,12,14
초기 문자열은 오름차순으로 정렬됩니다.
답변1
펄 사용:
perl -pe 's/\b(\d+)(?{$q=$1+1})(?:,(??{$q})\b(?{$p=$q++})){2,}/$1-$p/g'
(?{...})
이는 and 표현식을 통해 임베디드 Perl 코드와 함께 정규식을 사용하는 것입니다 (??{...})
. 첫 번째는 임베디드 코드만 평가하고 두 번째는 패턴으로 반환하는 값을 사용합니다. perlre(1)
전체 설명은 리소스를 참조하세요 .
두 개의 숫자만 포함하는 범위(예: -> )를 원하는 경우 수량자를 {2,}
로 바꾸세요.+
1,2,7
1-2,7
답변2
awk
다음은 쉼표로 구분된 정렬된 정수 목록을 반복하고 두 개의 배열을 채우는 짧은 스크립트입니다 a
.b
배열 에는 해당 끝 정수 a
와 함께 단조롭게 증가하는 각 정수 범위에 대한 시작 정수가 포함됩니다 . 코드의 변수는 발견된 범위의 수를 보유합니다.b
n
BEGIN {
OFS = FS = ","
}
{
n = 0
a[++n] = $1
for (i = 1; i < NF; ++i)
if ($i != $(i+1) - 1) {
b[n] = $i
a[++n] = $(i+1)
}
b[n] = $NF
$0 = ""
for (i = 1; i <= n; ++i)
if (a[i] == b[i])
$i = a[i]
else
$i = sprintf("%d-%d", a[i], b[i])
print
}
출력은 발견된 다양한 범위를 반복하고 n
각 필드가 단일 정수(길이 1 범위의 경우)이거나 범위의 시작과 끝을 나타내는 문자열인 레코드를 구성하여 생성됩니다.
제공한 데이터를 테스트하려면 파일에서 데이터를 읽으세요.
$ awk -f script.awk file
1-3,5-9,12,14
분명히 다음과 같이 표준 입력의 문자열을 사용하여 제공할 수 있습니다.
$ awk -f script.awk <<<"1,2,3,5,9,10,11,12,13"
1-3,5,9-13
답변3
대신 다음 zsh
과 같은 함수를 정의할 수 있습니다.
reduce() {
local i=1
argv=(${(nus:,:)1}) # split $1 on ",", numerically sort and remove dups
while ((i < $#)) {
if ((${argv[i]#*-} + 1 == ${argv[i+1]%-*})) {
argv[i]=${argv[i]%-*}-${argv[i+1]#*-}
argv[i+1]=()
} else {
((i++))
}
}
print ${(j:,:)@}
}
입력 범위도 허용됩니다.
$ reduce 1,2,3,5,6,7,8,9,12,14
1-3,5-9,12,14
$ reduce 1,2,3,5-7,8,9-11,12,13-20
1-3,5-20
$ reduce 5,2,4,5,6
2,4-6
입력 범위가 겹치면 제대로 작동하지 않습니다.
$ reduce 1-3,2
1-3,2
$ reduce 1-3,2-4
1-3,2-4
그로부터 bash
함수를 다음과 같이 정의할 수 있습니다.
reduce() { zsh -c '
i=1
argv=(${(nus:,:)1}) # split $1 on ",", numerically sort and remove dups
while ((i < $#)) {
if ((${argv[i]#*-} + 1 == ${argv[i+1]%-*})) {
argv[i]=${argv[i]%-*}-${argv[i+1]#*-}
argv[i+1]=()
} else {
((i++))
}
}
print ${(j:,:)@}' zsh "$@"
}
답변4
인식된 범위를 유지하기 위해 배열을 사용하여 다음 BASH 함수를 사용했습니다. 입력 문자열은 함수의 첫 번째 매개변수이고 결과는 두 번째 매개변수를 통해 다시 전달됩니다.
function compact_range {
arr=()
start=""
for cpu in ${1//,/ }; do
# Start a new range definition if necessary
[ -z "$start" ] && start=$cpu && range=$cpu && last=$cpu && continue
prev=$(( $cpu - 1 ))
# If the current CPU is not adjacent to the last CPU, start a new range
[ "$prev" -ne "$last" ] && arr+=($range) && start=$cpu && range=$cpu && last=$cpu && continue
# Current CPU is adjacent to an existing range, expand the current range
range="${start}-${cpu}" && last=$cpu
done
# Append the last range to the array of ranges
arr+=($range)
# Return a comma delimited list of ranges
eval $2=$(IFS=,;printf "%s" "${arr[*]}")
}
당신의 생각에 감사드립니다.