문제 일련의 숫자를 제공하는 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::IntSpan
libset-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