data.txt
다음과 같은 파일이 있습니다 .
1 aFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
2 bFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
3 cFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
4 dFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
5 eFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
6 fFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
7 gFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
8 hFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
9 iFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
10 jFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
11 kFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf IT524234
첫 번째 필드는 줄 번호입니다.
이제 일부 줄 번호 인수를 사용하여 스크립트를 호출할 수 있고 .txt에서 해당 줄 번호의 첫 번째 및 두 번째 필드를 인쇄할 수 있도록 쉘 스크립트를 작성하려고 합니다 data.txt
. 예를 들어 다음과 get.sh 1 3 5
같이 인쇄해야 합니다.
1 aFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
3 cFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
5 eFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
awk는 첫 번째와 두 번째 필드를 인쇄하는 데만 사용할 수 있다고 생각하는데 쉘 스크립트에 전달된 매개변수를 기반으로 특정 행만 필터링하는 데 갇혀 있습니다. 미리 감사드립니다.
답변1
awk에서는 줄 번호를 배열로 수집하고 파일을 한 번 읽고 배열에 언급된 줄을 인쇄할 수 있습니다.
#!/bin/sh
awk -v lines="$*" 'BEGIN { split(lines, a, "[, ]");
for (i in a) b[a[i]] = 1;}
NR in b {print $1, $2}' < data.txt
공백과 쉼표를 따라 변수를 배열로 분할 split()
하고 다음과 같이 루프에서 배열을 만듭니다.lines
a
for
b
열쇠이 배열에는 우리가 관심 있는 행이 포함되어 있습니다. 그런 다음 NR in b
현재 행 번호와 일치하는 키가 존재하는지 확인하십시오.
각 줄은 입력에 몇 번이나 존재하는지에 관계없이 한 번만 인쇄되며 줄은 인수에 지정된 순서가 아닌 입력 번호 순서로 인쇄됩니다.
$ bash get.sh 7 3 3
3 cFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
7 gFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
( get.sh 7,3,3
또한 유효함)
답변2
다음 줄을 텍스트 파일에 넣고 이름을 get.sh로 지정합니다. 그런 다음 실행 가능하게 만듭니다.
#!/bin/sh
## this is GNU sed
sed -En "
$(printf '%sbp\n' "$@" "d;")
:p;s/\S+/&\n/2;P
" data.txt
이제 다음과 같이 스크립트를 호출합니다.
chmod +x ./get.sh
./get.sh 1 3 5
답변3
#!/bin/bash
perl -le '
for (@ARGV) {
# separate command line args into filename(s) and line-number(s)
# line-numbers can be space and/or comma separated.
if (-e $_) { push @files, $_ } else { push @lines, split /,/};
};
@ARGV = @files;
$re = join("|",@lines);
while(<>) {
print join("\t",(split)[0..1]) if ($. =~ m/^($re)$/);
close(ARGV) if eof;
}' "$@"
이는 파일 이름이 아닌 매개 변수를 기반으로 정규식을 작성하며 나중에 각 파일의 줄 번호를 일치시키는 데 사용됩니다. 일치하는 경우 입력 줄을 공백으로 분할하고 처음 두 필드를 탭으로 구분하여 인쇄합니다.
close(ARGV)
지금까지 본 모든 입력의 줄 번호가 아니라 현재 파일의 줄 번호에 관심이 있기 때문에 필요합니다 . Perl은 파일 핸들이 닫힐 때만 변수를 재설정 $.
( $NR
또는 )하지만 파일 핸들은 일반적으로 루프에서 닫히지 $INPUT_LINE_NUMBER
않습니다 . while(<>)
이는 재설정될 수 있도록 파일 핸들을 명시적으로 닫습니다 $.
. 바라보다 perldoc -f eof
.
$ ./get.sh 1 3,5 data.txt
1 aFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
3 cFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
5 eFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
물론, 이 스크립트는 Perl "한 줄"을 둘러싼 무의미한 bash 래퍼가 아닌 Perl 스크립트여야 합니다. 그러나 사람들은 한 줄의 코드가 "올바른다"고 생각하는 것 같지만, #!/bin/bash 또는 #!/bin/sh 이외의 것을 인터프리터로 사용하는 스크립트는 다소 잘못된 것입니다.
#!/usr/bin/perl -l
for (@ARGV) {
# separate command line args into filename(s) and line-number(s)
# line-numbers can be space and/or comma separated.
if (-e $_) { push @files, $_ } else { push @lines, split /,/ };
};
@ARGV = @files;
$re = join('|',@lines);
while(<>) {
print join("\t",(split)[0..1]) if ($. =~ m/^($re)$/);
close(ARGV) if eof;
};
$ ./get.pl 1 3,5 data.txt
1 aFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
3 cFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
5 eFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
이것은 실제로정확히Perl 인터프리터만 포크하는 쉘 인터프리터를 포크하는 데 최소한의 시간과 메모리를 낭비하지 않는 것도 마찬가지입니다.
더 중요한 것은 다음과 같은 문제를 방지한다는 것입니다.쉘 참조쉘이 포함되어 있지 않기 때문입니다. 반품,구문 강조스크립트는 쉘 스크립트에서 작은따옴표로 묶인 문자열이 아니기 때문에 편집기에서는 잘 작동합니다. 그리고줄 번호경고/오류 메시지는 단일 라인 내의 상대 라인 번호가 아닌 스크립트 파일의 절대 라인 번호를 참조하기 때문에 스크립트를 디버깅할 때 정확합니다.
답변4
#! /bin/bash
# get.sh
IFS=$'\n'
args=(`sort -nu <<<$*`)
unset IFS
awk -v lines="${args[*]}" 'BEGIN{split(lines, ar, " ");}{ for (i in ar) { if (NR == ar[i]) print $1,$2} }' data.txt
먼저 args
정렬된 고유 값을 포함하는 배열을 만듭니다. 이를 위해 우리는 옵션 -n
과를 사용합니다. -u
보다자세한 내용은.
그런 다음 변수에서 split
배열을 만듭니다 . 이제 요소가 레코드 번호(NR)와 같으면 루프는 원하는 출력을 인쇄합니다.ar
lines
ar