
다음과 같은 파일이 있습니다.
H|ACCT|XEC|1|TEMP|20130215035845 849002|48|1208004|100|||1 849007|28|1208004|100|||1 T|2|3
파일 끝에는 추가 빈 줄이 있습니다.
비어 있지 않은 첫 번째 행과 마지막 행을 제외한 모든 행에서 열 5의 값을 열 4의 값으로 바꾸고 싶습니다.
마지막 행에는 다른 행만큼 많은 필드가 있을 수 있고 항상 숫자로 시작하는 수정되는 행에 의존할 수 없기 때문에 필드 수에 의존할 수 없습니다.
다음 코드를 시도했습니다.
awk 'BEGIN{FS="|"; OFS="|"} {$5=$4; print}' in.txt
출력은 다음과 같습니다
H|ACCT|XEC|1|1|20130215035845
||||
849002|48|1208004|100|100||1
||||
849007|28|1208004|100|100||1
||||
T|2|3||
||||
||||
||||
예상 출력:
H|ACCT|XEC|1|TEMP|20130215035845| 849002|48|1208004|100|100||1 849007|28|1208004|100|100||1 T|2|3
변경을 위해 비어 있지 않은 첫 번째 행과 마지막 행을 건너뛰려면 어떻게 해야 합니까? 또한 빈 줄을 건너뛰고 싶습니다.
답변1
awk
여기서는 파일을 한 번만 처리 하면 됩니다 .
awk -F'|' 'NR==1{print;next} m && NF{print m}
NF{l="\n"$0; $5=$4; m="\n"$0; c=0}; !NF{c++}
END{ print l; for (; i++<c;)print }' OFS='|' infile
설명하다:
여기서는 스카이프를 통해 첫 번째 줄을 전송하여 필드 5 의 값을 필드 4의 값으로 바꾼 다음 인쇄하고 실행합니다 next
.
...현재 다음 줄이 빈 줄이 아닌 경우(적어도 하나의 필드 포함 NF
) 전체 줄을 백업하고 먼저 \n
ewline을 추가한 다음 5번째 필드l="\n"$0
의 값 과 4번째 필드 의 값을 설정하고 마지막으로 ewline을 추가하여 변수로 설정합니다 . 다음과 같은 변수가 있습니다.$5=$4
m
\n
m="\n"$0;
c
카운터!NF{c++}
하나 이상의 필드가 있는 행이 표시되지 않는 경우 빈 행 수를 결정하는 데 사용되는 플래그입니다. 그렇지 않으면 c=0
이 카운터가 재설정됩니다.
m
이제 변수의 행을 수정했으며 m && NF{print m}
다음 awk
실행 에서 m
설정한 위치에 인쇄 할 것이며 빈 행이 아닙니다 & NF
(빈 행이 있을 때 반복 인쇄를 방지하기 위해 사용됩니다).
마지막으로 교체를 수행하기 전에 매번 백업하는 손대지 않은 마지막 줄을 인쇄한 END{ print l; ...
다음 루프 필드가 있는 줄을 본 적이 없는 빈 줄의 수를 인쇄합니다 for (; i++<c;)print }'
.
추가 빈 줄이 필요하지 않으면 훨씬 짧아집니다.
awk -F'|' 'NR==1{print;next} m && NF{print m}
NF{l=$0; $5=$4; m=$0} END{ print l}' OFS='|' infile
답변2
의 경우 sed
두 번째 행이 비어 있다고 가정합니다.
sed '1{n;d;};/./!{H;$g;$p;d;};x;s/|/\n/4;s/\([^|]*\)\n[^|]*/\1|\1/'
sed
대체가 무엇을 의미하는지 이해하지 못하는 경우 \n
대신 리터럴 개행 문자를 사용하십시오(또는 파일에 속하지 않는 것으로 알려진 문자를 사용하십시오).
설명하다:
첫 번째 줄을 제외한 줄은 예약된 공간에 수집되어 파일 끝에 도달하면 있는 그대로 인쇄되고, 그렇지 않으면 필요한 대체 항목으로 인쇄됩니다.
상세히:
1{n;d;}
: 첫 번째 줄은n
그대로 인쇄하고, 다음 줄을 읽고,d
삭제하면 됩니다. 왜? 예약된 공간에는 인쇄할 내용이 포함되어 있으므로 어쨌든 빈 줄이 포함됩니다././!{H;$g;$p;d;}
빈 줄에서만 수행되며H
이전 공간에 추가됩니다. 마지막 줄에 대해서만$
예약된 공간을 뒤로 이동하고 인쇄합니다. 어쨌든d
해당 줄의 추가 실행을 중지하려면 삭제하세요.x
비어 있지 않은 라인을 보유 버퍼와 교환하여 거기에 유지하고, 비어 있지 않은 마지막 라인이 아니라는 것을 알기 때문에 이제 저장된 라인을 처리할 수 있습니다.s/|/\n/4;s/\([^|]*\)\n[^|]*/\1|\1/
네 번째 열을 줄바꿈 문자로 바꿔 표시하여|
열 4에서 열 5로 복사를 수행 한 다음 일치 전후의 필드를 이전 필드 크기의 두 배로 바꿉니다.
답변3
내가 말했듯이 가장 쉬운 방법은 파일을 두 번 처리하는 것입니다.
첫 번째 패스 - 줄 번호를 가져옵니다. 비어 있지 않은 마지막 줄에 대해.
두 번째 단계 - 비어 있지 않은 마지막 행 앞에 필드가 5개 이상 있는 모든 행(헤더 제외)을 처리합니다.
awk -F'|' -vc=0 'NR==FNR{if (NF){c=NR};next};
FNR>1 && NF>4 && FNR<c {$5=$4};1' OFS='|' infile infile
답변4
행에 4개의 열만 있으면 어떻게 될까요? 다섯 번째와 네 번째 열의 값을 추가해야 한다고 가정했습니다. 옳은?
첫 번째 버전 - awk 사용
awk '
BEGIN {
FS = "|";
OFS = "|";
}
FNR == NR && $0 {
last = NR;
}
FNR != NR {
if(NF > 3 && FNR != last && FNR != 1) {
$5 = $4;
}
print;
}' input.txt input.txt
동일한 코드와 주석:
awk '
BEGIN {
FS = "|";
OFS = "|";
}
# The first traversing through file
# It is needed for getting the number of the last, non-empty line
FNR == NR && $0 {
last = NR;
}
# The second traversing through file
FNR != NR {
# if the number of fields more than 3 (therefore, the fourth column exists)
# and the line number of the current file is not the last and not the first.
if(NF > 3 && FNR != last && FNR != 1) {
$5 = $4;
}
print;
}' input.txt input.txt
두 번째 버전 - sed 및 tac 사용
tac input.txt |
sed '
1,/./!{
$!{
s/\(|\w*\)/\1\1/3
s/|\w*//5
}
}' | tac
설명하다:
tac
- 연결을 반대로 하고 파일을 인쇄합니다.tac
그 반대이다cat
.1,/./!
- 첫 번째 행에서 비어 있지 않은 첫 번째 행(포함)까지 행을 건너뜁니다.$!
- 마지막 줄을 제외한 모든 줄. 파일을 뒤집었고 마지막 줄이 실제로 첫 번째 줄이라는 것을 기억하세요.s/\(|\w*\)/\1\1/3
- 네 번째 열을 복제합니다. 미용\w
보다는 사용하기로 결정했습니다[^|]
. 그러나 필드에 단어가 아닌 문자가 필요한 경우 이를 변경할 수 있습니다.s/|\w*//5
- 이전의 다섯 번째 열이 제거되었습니다(현재는 여섯 번째 열).| tac
- 파일을 다시 뒤집으세요.