송장 파일을 전처리하고 각 송장을 별도의 파일로 분리하려고 합니다. 송장은 여러 페이지로 구성될 수 있습니다. 각 페이지의 제목은 다음과 같습니다.
121084
A134
09.17.19
1
각 페이지 상단에는 6개의 빈 줄이 있고 그 뒤에 송장 번호, 고객 번호, 날짜, 페이지 번호(그리고 송장의 나머지 부분)가 표시됩니다.
각 페이지의 10번째 줄(각 페이지의 10번째 줄은 송장 페이지 번호)의 "1"(70개의 공백 뒤에 "1"이 옴)마다 1번째 줄에 일부 텍스트를 삽입해야 합니다(구분자로 사용됨). 분할 파일). 송장은 여러 페이지로 구성될 수 있지만 "1"(70개의 공백 뒤에 "1"이 옴)은 새 송장임을 의미합니다.
"1"이 나타나면(70개의 공백 뒤에 "1"이 옴) 그 위의 9줄(이 청구서의 첫 번째 줄) 빈 줄에 텍스트를 삽입하고 싶습니다. 파일의 모든 항목에 대해 이 작업을 수행합니다. 그런 다음 각 송장에 대한 파일을 별도의 파일로 분할할 수 있습니다.
sed를 사용하여 패턴 바로 앞에 데이터를 삽입할 수 있다는 것을 알고 있지만 그 위에 9행 데이터를 어떻게 삽입합니까?
나는 보통 sed와 awk로 그것을 할 수 있지만 이것은 나를 당황하게 합니다.
답변1
당신이 말하고 싶은 것이 다음과 같다고 가정해보자:
여러 송장이 포함된 긴 파일이 있습니다. 모든 청구서는 텍스트로 시작됩니다 70 spaces and a 1
. 각 송장 내용 앞에 새 내용 9줄을 삽입해야 합니다.
그러면 10개의 행을 모으기만 하면 됩니다. 현재 줄이 다음과 같을 때송장 시작첫 번째 줄에 새로운 텍스트를 삽입합니다.
실제로 이는 다음과 같이 달성할 수 있습니다.
sed -e '1{N;N;N;N;N;N;N;N}' -e 'N;l;/\n \{70\}1$/{iNew content here' -e 'P};P;D' file
또는 긴 형식(일부 sed 구현에는 주석이 적용되지 않음):
sed '1{ # (only) on the first line
N;N;N;N;N;N;N;N # accumulate 9 lines (first one plus 8 more).
}
N # On every line,also accumulate that line
/\n \{70\}1$/{ # If buffer ends ($) in 70 spaces and a "1"
i\
New content added here # insert the content of this line at
# the start of the buffer (10 lines above).
P # and then print it.
}
P;D # close the N cycle above by
# printing and deleting one line
' file # On the selected file.
해결책은 awk
송장의 시작을 표시하는 줄에 RS를 설정하는 것입니다. 이 경우 FS를 개행으로 설정하면 각 행이 필드로 분할되고 $(NF-9)는 위의 9번째 행을 참조합니다.
awk -v ln=9 '
( NF < ln ){ print; next }; # not enought fields?
# include
{ $(NF-ln) = "New Text to include" newln $(NF-ln);
print # and print
};
BEGIN{ breakln = sprintf("%71s",1); # 70 spaces and a 1
newln = sprintf("\n"); # a newline
RS = breakln newln; # set the Record Separator
FS = "\n"; # set the Field separator
OFS = FS; # print what got removed
ORS = RS # print what got removed
}
' file
또는 (쉘/awk 솔루션):
breakln="$(printf '%71s' 1)";
newl=$'\n';
awk ' (NF<ln){print;next};
{ $(NF-ln)="New Text to include\n"$(NF-ln); print}' \
ln=10 \
RS="$breakln$newl" \
FS="$newl" \
OFS="$newl" \
ORS="$breakln$mewl" \
file
답변2
awk
+ 로 tac
:
tac file | awk -v delim="--split page here--" '{
if (nextnr=="" && $1~/^[0-9]+$/ && $0==" "$1) {
nextnr=NR+9 # pagenr found, remember next position
}
else if (NR==nextnr) {
$0=delim # overwrite line with delimiter
nextnr="" # reset
}
print
}' | tac
먼저 역방향 파일을 사용하여 tac
페이지 번호를 위에서 아래로 검색하고 구분 기호를 삽입할 수 있습니다.
이러한 조건이 충족되면 검색은 if 절로 시작됩니다.
nextnr
(초기) 설정되지 않았습니다(nextnr==""
). 다음 구분 기호의 줄 번호를 보유하는 변수입니다.- 첫 번째 필드는 숫자(
$1~/^[0-9]+$/
) 입니다. - 줄에는 70개의 공백과 숫자가 포함되어 있습니다.
세 가지 조건이 모두 true인 경우 nextnr
현재 행 번호(레코드 수)로 설정됩니다 NR + 9
.
현재 줄이 구분 기호( )가 있는 줄인 경우 NR==nextnr
해당 줄을 구분 기호로 덮어쓰고 재설정합니다 nextnr
.
스크립트의 마지막 줄은 현재 줄(원래 줄 또는 구분 기호로 덮어쓰기)을 인쇄합니다.
마지막 단계에서는 출력이 다시 반전됩니다 tac
.
답변3
이것은 스크립트 가능한 편집기 솔루션입니다. 파일의 "(70 공백) 1" 줄 수만을 기준으로 파일에 송장이 몇 개 있는지 알아내는 것이 아이디어입니다. 그러면 스크립트가 여러 번 반복되어 명령을 출력합니다 ed
. 루프는 "(70 공백) 1'이 있는 줄 앞의 줄 9"를 점선 절단선으로 변경하는 데 충분한 명령을 출력합니다. -----8<-----
선행 마침표( 대체 문자열의 끝을 구별하는 데 사용됨) ed
를 제외한 다른 것으로 텍스트를 바꿉니다 . 송장( i < count
)을 반복하는 경우 변경 후 10줄 앞으로 이동하여 방금 스크린샷을 찍은 페이지를 재발견하지 않도록 하세요. 루프( i == count
)를 완료한 다음 ed의 "write and quit" 명령을 인쇄하면 모든 printf/echo 출력이 파이프로 전송되고 옵션이 ed
입력으로 읽혀집니다. -s
"자동" 모드 - ed
읽거나 쓴 바이트가 보고되지 않습니다.
#!/usr/bin/bash
count=$(grep -c '^ 1' input)
for((i=1; i<=count; i++))
do
printf '%s\n' '/^ 1/-9c'
printf '%s\n' '-----8<-----' '.'
[[ $i < $count ]] && printf '%s\n' '+10'
[[ $i == $count ]] && echo wq
done | ed -s input