ファイルを変更するときに最初、最後の空でない行と空白行をスキップする方法は?

ファイルを変更するときに最初、最後の空でない行と空白行をスキップする方法は?

次のファイルがあります。

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

...現在、次の行が空白行ではない場合(少なくとも1つのフィールドを含むNF)、行全体をバックアップして最初に\newlineを追加し、次に5番目のフィールドl="\n"$0の値と4番目のフィールド値を設定し、最後にewlineを追加します。変数に設定します。次の変数があります。$5=$4m\nm="\n"$0;cカウンター!NF{c++}1つ以上のフィールドを持つ行が表示されない場合に空の行数を決定するために使用されるフラグ。それ以外の場合、c=0このカウンタはリセットされます。

mこれで変数の行を修正し、次の実行で設定した場所に印刷し、空の行ではありません(空の行があるときにm && NF{print m}繰り返し印刷を防ぐために使用されます)。awkm& 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

の場合、sed2番目の行が空であるとします。

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 番目の列を改行文字で置き換えて、|列 4 から列 5 にコピーを実行し、一致の前後のフィールドを前のフィールドサイズの 2 倍に置き換えます。

答え3

私が言ったように、最も簡単な方法はファイルを2回処理することです。
最初のパス - 行番号を取得します。空でない最後の行について。
2番目のステップ - 空でない最後の行の前に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つの列しかないとどうなりますか? 5番目と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

2番目のバージョン - sedとtacの使用

tac input.txt | 
sed '
1,/./!{
    $!{
        s/\(|\w*\)/\1\1/3
        s/|\w*//5
    }
}' | tac 

説明する:

  1. tac- 接続を逆にしてファイルを印刷します。tacその逆だcat
  2. 1,/./!- 最初の行から空でない最初の行(含む)まで行をスキップします。
  3. $!- 最後の行を除くすべての行。ファイルを裏返し、最後の行が実際に最初の行であることを覚えておいてください。
  4. s/\(|\w*\)/\1\1/3- 4番目の列を複製します。美容\wではなく使用することにしました[^|]。ただし、フィールドに単語以外の文字が必要な場合は変更できます。
  5. s/|\w*//5- 以前の5番目の列が削除されました(現在は6番目の列)。
  6. | tac- ファイルを再び反転します。

関連情報