열 1에서 마지막으로 나타나는 문자열을 찾아 이를 열 3의 해당 값으로 바꾸려면 어떻게 해야 합니까?

열 1에서 마지막으로 나타나는 문자열을 찾아 이를 열 3의 해당 값으로 바꾸려면 어떻게 해야 합니까?

내 파일에는 세 개의 열이 있습니다.

apple1        10109283      20012983
apple1        10983102      10293809
apple1        10293893      2349823049
apple10       109283019     109238901
apple10       192879234     234082034
apple10       234908443     3450983490

열 1(이 경우 행 3 또는 6)에서 이 문자열의 마지막 항목을 찾고 열 3의 해당 숫자를 다른 숫자로 바꾸고 싶습니다. 예(행 3, 열 3을 444444444로 교체"

apple1        10109283      20012983
apple1        10983102      10293809
apple1        10293893      444444444
apple10       109283019     109238901
apple10       192879234     234082034
apple10       234908443     3450983490

지금까지 sed를 사용해 보았지만 작동하지 않습니다.

sed '$s/apple1*$/444444444/'

답변1

파이프가 없는 순수한 sed솔루션tac

이와 같은 경우 행별 접근 방식은 sed도움이 되지 않습니다. -zGNU의 옵션 처럼 전체 버퍼를 한 번에 처리하는 것이 더 좋습니다 ( 휴대용 대안을 보려면 sedlinux 및 GNU를 사용하는 것 같습니다.sed이 Q&A).

이제 욕심 많은 성격을 활용할 수 있습니다 . 다른 모든 발생은 먹힐 것이기 때문에 .*패턴은 .*apple1마지막 발생을 포함하여 모든 것과 일치합니다 .apple1.*

그런 다음 다음 필드( \s+열 구분 기호, [0-9]+두 번째 열 및 다른 열 \s+, 모든 GNU 확장 정규 표현식)를 추가하고 ()이를 대체하여 재사용할 수 있도록 합니다 \1. 그런 다음 외부에 세 개의 열을 추가하여 ()대체하면 결과는 다음과 같습니다.

sed -zE 's/(.*\napple1\s+[0-9]+\s+)[0-9]+/\14444444/'

그게 다야.

GNU를 사용하지 않는 사용자를 sed위한 참고 사항 :휴대용 솔루션은 덜 편리합니다.

sed -E 'H;1h;$!d;x;s/(.*\napple1[[:space:]]+[0-9]+[[:space:]]+)[0-9]+/\14444444/'

답변2

tac file |
awk -v string='apple1' -v replace='444444444' '
    !flag && $1 == string { $3 = replace; flag = 1 }
                          { print }' |
tac

tac파이프라인은 먼저 GNU coreutils를 사용하여 데이터의 행 순서를 반대로 바꿉니다. 마지막 행은 첫 번째 열이 특정 문자열이 있는 위치이므로 찾기가 더 쉽습니다.

awk명령은 단순히 첫 번째 열을 주어진 문자열과 비교하고, 아직 대체를 수행하지 않은 경우( !flag0이 아닌 경우) 첫 번째 열에서 문자열을 찾으면 세 번째 열을 수정합니다. 이 작업을 수행할 때 flag더 이상 대체가 이루어지지 않도록 1로 설정합니다 .

프로그램의 나머지 부분은 awk현재 줄(수정된 줄 포함)만 인쇄합니다.

파이프라인의 끝에서 우리는 다시 라인의 순서를 반대로 바꿉니다 tac.

질문의 데이터를 고려하면 출력은 다음과 같습니다.

apple1        10109283      20012983
apple1        10983102      10293809
apple1 10293893 444444444
apple10       109283019     109238901
apple10       192879234     234082034
apple10       234908443     3450983490

열 3의 수정으로 인해 수정된 행의 열은 다른 행의 열과 약간 다릅니다. 더 보기 좋게 만들기 위해 column -t파이프라인 끝의 추가 단계에 결과를 전달할 수 있습니다. 이렇게 하면 출력은 다음과 같습니다.

apple1   10109283   20012983
apple1   10983102   10293809
apple1   10293893   444444444
apple10  109283019  109238901
apple10  192879234  234082034
apple10  234908443  3450983490

열 사이에 공백이 여러 개 있습니다.


의 경우 sed문자열이 첫 번째 열에 나타나는 첫 번째 행의 세 번째 열을 바꾸는 것만큼 쉽지 않습니다(위의 파이프라인에서와 같이 데이터 행을 반대로 가정한다고 가정). 우리도 해야 한다아니요첫 번째 열이 문자열과 일치하더라도 후속 행의 세 번째 열을 바꿉니다.

다음은 sed이 작업을 올바르게 수행하는 편집 스크립트입니다(작동하는 여러 변형이 있을 수 있음).

/^apple1\>/ ! {
        p
        d
}

s/[[:digit:]]*$/444444444/

:loop
n
$ ! b loop

apple1첫 번째 부분은 첫 번째 열과 일치하지 않는 입력 시작 부분에 줄을 인쇄하는 일을 담당합니다 . 표현식에서 \>단어의 끝과 일치하므로 실수로 일치 하거나 나타날 수 있는 다른 유사한 문자열이 apple1발생하지 않습니다 . 입력 시작 부분의 각 줄이 실행되고 (print ) 및 (delete + 스크립트 상단의 다음 줄로 계속)apple10apple12pd{ ... }아니요표현식을 일치시킵니다.

s명령(대체)은 입력의 첫 번째 줄에 대해 실행됩니다.하다apple1줄의 시작 부분에서 일치합니다 . 줄 끝의 숫자 문자열을 s 로 바꿉니다 4.

loop그런 다음 현재 줄을 인쇄하고 (do print and read)를 사용하여 다음 줄을 읽어 수정되지 않은 나머지 데이터를 전달하는 역할을 담당하는 표시된 부분이 있습니다 . "현재 줄"은 루프를 처음 통과하는 명령에 의해 수정됩니다.nns

loop입력의 마지막 줄에 도달하지 못한 경우 마지막 줄은 레이블로 다시 분기됩니다.

예제를 실행하세요:

$ tac file | sed -f script.sed | tac
apple1        10109283      20012983
apple1        10983102      10293809
apple1        10293893      444444444
apple10       109283019     109238901
apple10       192879234     234082034
apple10       234908443     3450983490

답변3

다음 명령을 사용해보십시오. 훌륭하게 작동합니다.

for i in `awk '{print $1}' file1| awk '{if(!seen[$1]++)print }'`; do j=`awk -v i="$i" '$1 == i {print $0}' file1| awk '{print NR}'| sed -n '$p'`; awk -v i="$i" '$1 == i {print $0}' file1|awk -v i="$i" -v j="$j" 'NR==j{$3="444444444"}1'; done

관련 정보