쉼표로 구분된 숫자 범위를 시작부터 끝까지 순서대로 축소합니다.

쉼표로 구분된 숫자 범위를 시작부터 끝까지 순서대로 축소합니다.

문제 일련의 숫자를 제공하는 BASH 스크립트를 해결/강화하려고 합니다. 프로세서 바인딩을 위해 numactl에 입력할 물리적 프로세서 번호를 추출하기 위해 토폴로지 인식 도구(lstopo-no-graphics)를 사용하고 있습니다.

L3 L#4 공유 캐시 물리적 코어에 대한 출력 예

lstopo-no-graphics --no-io|sed -n "/L3 L#3/,/L3/p"|grep -v "L3\|L2"|tr -s '[:space:]'|cut -d " " -f4|grep -o "[0-9]*"|sort -g|tr '\n' ','|sed '$s/,$//'

결과는 숫자 계열 문자열입니다.

32,33,34,35,36,37,38,39,96,97,98,99,100,101,102,103

모든 것이 괜찮습니다. 저는 이 시리즈를 ‌ 으로 사용하고 있으며 numactl --physcpubin=32,33,34,35,36,37,38,39,96,97,98,99,100,101,102,103시퀀스를 ‌ 로 축소할 수 있기를 원합니다 numactl --physcpubin=32-39,96-103. 여러 개의 쉼표로 구분된 숫자 시퀀스를 "an" 시리즈로 축소하고 각 시퀀스가 ​​연속적일 때 쉼표로 구분되도록 하고 싶습니다.

기존 bash 스크립트에는 문제가 없습니다. 단지 더 깔끔한 구현을 찾고 있습니까?(아이디어가 있는 사람이 있는 경우)

답변1

이것을 다른 이름으로 저장하세요range.awk.

{
    for(i=2;i<=NF+1;i++){     #Visit each number from the 2nd on
        if($i==$(i-1)+1){
            if(f=="")f=$(i-1) #Candidate to first number of a range
            continue
        }
        printf("%s%s%s%s", f, (f!="" ? "-" : ""), $(i-1), (i>NF ? RS : FS))
        f="" #Unset the candidate
    }
}

달리다: awk -F, -f range.awk.

또는 축소된 한 줄을 복사하여 붙여넣습니다.

awk -F, '{for(i=2;i<=NF+1;i++){if($i==$(i-1)+1){if(f=="")f=$(i-1);continue}printf("%s%s%s%s",f,f!=""?"-":"",$(i-1),i>NF?RS:FS);f=""}}'

필드 구분 기호를 하드코딩하지 않았으므로 -F.

출력 예:

$ awk -F, -f range.awk <<< 32,33,34,35,36,37,38,39,96,97,98,99,100,101,102,103
32-39,96-103
$ awk -F, -f range.awk <<< 0,1,2,5,8,9,11
0-2,5,8-9,11
$ awk -F, -f range.awk <<< 4
4

답변2

Perl 한 줄의 코드 사용설정::IntSpan기준 치수:

$ perl -MSet::IntSpan -l -e 'print Set::IntSpan->new(shift)' 32,33,34,35,36,37,38,39,96,97,98,99,100,101,102,103
32-39,96-103

이는 명령줄에서 쉼표로 구분된 정수 목록인 하나의 인수를 사용합니다. 목록에 공백, 탭 또는 기타 공백이 있는 경우 이를 따옴표로 묶을 수 있습니다. Set::IntSpan극도로숫자 목록의 공백을 무시하면 모든 공백이 무시됩니다.

목록에 이미 범위와 정수가 혼합되어 있으면 원활하게 처리됩니다.

$ perl -MSet::IntSpan -l -e 'print Set::IntSpan->new(shift)' 32,33,34-38,39,96-100,101,102,103
32-39,96-103

Set::IntSpanlibset-intspan-perl패키징은 Debian 및 Ubuntu 및 Fedora와 같은 관련 배포판에서 동일합니다 perl-Set-IntSpan. 다른 시스템의 경우 패키지를 찾을 수 없으면 를 사용할 수 있습니다 cpan.

스크립트에서 사용하려면 다음을 사용할 수 있습니다.명령 대체:

numactl --physcpubin=$(perl -MSet::IntSpan -l -e 'print Set::IntSpan->new(shift)' 32,33,34,35,36,37,38,39,96,97,98,99,100,101,102,103)

스크립트에서 한 번만 사용한다면 괜찮지만, 그렇지 않으면 지루하고 가독성이 떨어집니다. 따라서 이를 bash 스크립트의 함수로 래핑합니다(약간의 개선으로 선택적으로 명령줄에서 여러 인수를 사용할 수 있습니다. 예를 들어 배열을 CPU 세트로 채우려는 경우에 유용합니다).

collapse () {
 perl -MSet::IntSpan -le 'for (@ARGV) {print Set::IntSpan->new($_)}' "$@"
}

그런 다음 다음과 같이 사용하십시오.

cpus=$(collapse 32,33,34-38,39,96-100,101,102,103)
numactl --physcpubin="$cpus"

또는

numactl --physcpubin=$(collapse 32,33,34,35,36,37,38,39,96,97,98,99,100,101,102,103)

이는 명령줄, 명령줄에 나열된 파일 또는 표준 입력에서 직접 여러 인수를 가져올 수 있는 보다 세련된 버전의 독립 실행형 스크립트입니다. 또는 이들의 조합. 제공된 순서대로 여러 인수를 처리하고 STDIN을 마지막으로 처리합니다. 파일 및 STDIN의 입력은 한 번에 한 줄씩 처리됩니다.

#!/usr/bin/perl

use strict;
use Set::IntSpan;

my @series = ();

# take args from files and from command line
foreach my $arg (@ARGV) {
  if ( -e $arg ) { # if the arg is a filename, slurp it in
    open(my $fh, "<", $arg) || die "couldn't open $arg: $!\n";
    while(<$fh>) { push @series, $_; }
  } else { # otherwise, treat the arg as a series
    push @series, $arg;
  }
};

# take args from stdin too, if stdin isn't a terminal
if (! -t STDIN) { while(<STDIN>) { push @series, $_; } };

foreach (@series) {
  print Set::IntSpan->new($_) . "\n";
};

예를 들어 다른 이름으로 저장하고 collapse.pl실행 가능하게 만들고 chmod +x collapse.pl다음과 같이 실행하십시오.

$ printf '1,2,3\n4,5,6' | ./collapse.pl 7,8,9 32-39,50,51,52,53
7-9
32-39,50-53
1-3
4-6

관련 정보