請求書ファイルを前処理し、各請求書を別々のファイルに分割しようとします。請求書は複数のページで構成できます。各ページのタイトルは次のとおりです。
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個のスペースと数字が含まれています。
3つの条件がすべて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