awk가 일치 후 다음 열을 제공하도록 만드는 방법

awk가 일치 후 다음 열을 제공하도록 만드는 방법

다음 파일(somefile.txt)이 있습니다.

/A/1/B/1/C/1/D/1/E/1/F/2/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/5/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/9/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/7/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1

나는 다음 결과를 원합니다 (뒤의 다음 숫자 F):

2
5
9
7
8
3
6
8
3
6

행당 열 수가 가변적이라면 다음과 같이 할 수 있는 방법이 있습니까? :

awk -F'/' '/F/ {print <column_of_match> + 1 }' somefile.txt

답변1

Perl을 사용하면 배열 분할이 편리하므로 배열의 각 요소 쌍을 해시의 키와 값으로 처리할 수 있습니다.

$ perl -F/ -lane '%f = @F[1..$#F]; print $f{F}' input.txt 
2
5
9
7
8
3
6
8
3
6

Perl -F-a(자동 분할)은 awk와 유사하게 작동하지만 행을 자동으로 $1, $2, $3 등으로 분할하는 대신 각 행을 이라는 배열로 자동 분할합니다 @F.

이 스크립트는 @F배열 슬라이스(0번째 요소를 제외한 모든 요소)를 이름이 지정된 해시(연관 배열)로 변환하고 %f키 "F"를 사용하여 요소를 인쇄합니다.%f


기능/작동 방식(그리고 빈 문자열인 @F의 0번째 요소를 제외해야 하는 이유)을 강조하기 위해 사용 @F시 다음 %f과 같습니다 .데이터::덤프모듈 dump기능:

$ perl -F/ -MData::Dump=dump -lane '
    %f = @F[1..$#F];
    print join("\n", $_, dump(@F), dump(\%f), $f{F}), "\n"' input.txt 
/A/1/B/1/C/1/D/1/E/1/F/2/G/1/H/1/I/1/J/1/K/1/
("", "A", 1, "B", 1, "C", 1, "D", 1, "E", 1, "F", 2, "G", 1, "H", 1, "I", 1, "J", 1, "K", 1)
{ A => 1, B => 1, C => 1, D => 1, E => 1, F => 2, G => 1, H => 1, I => 1, J => 1, K => 1 }
2

/B/1/C/1/D/1/E/1/F/5/G/1/H/1/I/1/J/1/K/1/
("", "B", 1, "C", 1, "D", 1, "E", 1, "F", 5, "G", 1, "H", 1, "I", 1, "J", 1, "K", 1)
{ B => 1, C => 1, D => 1, E => 1, F => 5, G => 1, H => 1, I => 1, J => 1, K => 1 }
5

/C/1/D/1/E/1/F/9/G/1/H/1/I/1/J/1/K/1/
("", "C", 1, "D", 1, "E", 1, "F", 9, "G", 1, "H", 1, "I", 1, "J", 1, "K", 1)
{ C => 1, D => 1, E => 1, F => 9, G => 1, H => 1, I => 1, J => 1, K => 1 }
9
...and so on...

F참고: 입력 내용이 없으면 빈 줄이 인쇄됩니다. 이것이 원하는 것이 아닐 경우 다음을 수행하십시오.

perl -F/ -lane '%f = @F[1..$#F];
                if (defined $f{F}) {
                  print $f{F}
                } else {
                   print STDERR "Error on input line $.: F has absconded"
                }' input.txt

답변2

사용된 답변은 다음과 같습니다 sed.

$ sed -n 's|.*F/\([0-9]\).*|\1|p' <<EOF
/A/1/B/1/C/1/D/1/E/1/F/2/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/5/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/9/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/7/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1
EOF
2
5
9
7
8
3
6
8
3
6

설명 -n 's|.*F/\([0-9]\).*|\1|p':

  • -n명시적으로 지시하지 않는 한 아무것도 인쇄하지 않는다는 의미입니다.
  • p표현식의 꼬리는 "이 표현식이 일치하면 이 줄을 인쇄합니다"를 의미합니다. 이는 기호가 없는 행은 F/[0-9]인쇄되지 않음을 의미합니다.
  • s|foo|bar|표현의 의미는 다음 foo과 같습니다. 로 대체합니다 bar. 라고 생각하시곤 하는데, s/foo/bar/표현에 a가 있어서 피하곤 했어요./|
  • 경쟁부분 ( foo):
    • .*F/[0-9].*의미: F/숫자가 포함된 모든 줄.
    • .*F/\([0-9]\).*F/의미: 숫자가 포함된 전체 줄과 일치하지만 해당 숫자를 기억하세요.
  • 교체 부품 ( bar):
    • \1우리가 기억하는 숫자를 나타냅니다.

간단히 말해서:

  • 일치하는 줄을 찾아 *F/[0-9]*숫자로만 바꿉니다.

여러 자리 양의 정수를 사용할 수 있으면 표현식을 쉽게 조정할 수 있습니다.

sed -n 's|.*/F/\([0-9]\+\)/.*|\1|p'

답변3

구분 기호와 일치하는 패턴을 사용하여 해당 하위 문자열을 배열로 분할하고 F하위 필드를 인쇄하면 됩니다.

테스트 코드:

$ awk 'match ($0, "/F/[^/]/") {
    split (substr ($0, RSTART, RLENGTH), V, "/");
    print V[3];
}' Match.txt

필드를 반복하거나 두 가지 프로세스를 사용할 필요가 없습니다.

문자열 인덱스를 조정하여 split필요하지 않은 부분을 제거할 수도 있지만 이렇게 하면 활용도가 떨어지고 일회성 오류가 발생할 가능성이 높아집니다.

awk 'match ($0, "/F/[^/]/") {
    print substr ($0, RSTART+3, RLENGTH-4);
}' Match.txt

답변4

다음은 awk를 두 번 사용하는 것과 관련된 문제에 대한 가능한 해결책입니다. 한 번은 올바른 위치에서 분할하고 다음 번에는 숫자를 가져와 인쇄합니다.

스크립트는 다음과 같습니다.

awk -F "/F/" '{print $2}' prova.txt  | awk -F "/" '{print $1}'

첫 번째 부분에서는 두 번째 부분의 첫 글자가 우리가 찾고 있는 숫자가 되도록 입력 문자열을 분할하고 /F/스크립트의 두 번째 부분에서는 해당 숫자를 분리합니다.

이것은 한 줄에 최대 하나만 있을 때 작동합니다 F(빈 줄만 인쇄하므로 F 없이도 작동합니다.

관련 정보