내 파일에는 세 개의 열이 있습니다.
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
도움이 되지 않습니다. -z
GNU의 옵션 처럼 전체 버퍼를 한 번에 처리하는 것이 더 좋습니다 ( 휴대용 대안을 보려면 sed
linux 및 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
명령은 단순히 첫 번째 열을 주어진 문자열과 비교하고, 아직 대체를 수행하지 않은 경우( !flag
0이 아닌 경우) 첫 번째 열에서 문자열을 찾으면 세 번째 열을 수정합니다. 이 작업을 수행할 때 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 + 스크립트 상단의 다음 줄로 계속)apple10
apple12
p
d
{ ... }
아니요표현식을 일치시킵니다.
이 s
명령(대체)은 입력의 첫 번째 줄에 대해 실행됩니다.하다apple1
줄의 시작 부분에서 일치합니다 . 줄 끝의 숫자 문자열을 s 로 바꿉니다 4
.
loop
그런 다음 현재 줄을 인쇄하고 (do print and read)를 사용하여 다음 줄을 읽어 수정되지 않은 나머지 데이터를 전달하는 역할을 담당하는 표시된 부분이 있습니다 . "현재 줄"은 루프를 처음 통과하는 명령에 의해 수정됩니다.n
n
s
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