「|」の出現箇所を置き換えたい[〜#〜] except [〜#〜]sedのみを使用してスペースのあるファイルのすべての行の最後の行。これを避けたい:
sed -e "s/[|]/ /1" -e "s/[|]/ /1" -e "s/[|]/ /1" -e "s/[|]/ /1" -e "s/[|]/ /1" -e "s/[|]/ /1" -e "s/[|]/ /1" mydata.txt
ファイル入力:
FLD1 |SFK TK |FLD2 |FLD4 |FLD5 |- |20200515 |NNNN |406 RCO 301
FLD1 |SFK TK |FLD2 |FLD4 |FLD5 |- |20200515 |NNNN |0
FLD1 |SFK TK |FLD2 |FLD4 |FLD5 |- |20200515 |NNNN |0
ファイル出力:
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |406 RCO 301
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
sed ':a;/[|].*[|]/s/[|]/ /;ta' file
/[|].*[|]/
:行に2つのパイプがある場合、s/[|]/ /
:最初のものをスペースに置き換えます。ta
:置換が行われた場合は、:a
に戻ります。出力:
$ sed ':a;/[|].*[|]/s/[|]/ /;ta' file
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |406 RCO 301
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
@steeldriverが述べたように、上記の場合のように、基本正規表現(BRE)では|
の代わりに単に[|]
を使用できます。 -E
フラグをsedに追加すると、拡張正規表現(ERE)が有効になり、[|]
または\|
を書き込む必要があります。
完全を期すために、 POSIX sed仕様 は、「以外の編集コマンド{...}、a、b、c、i、r、t、w、 :、および#の後にセミコロンを付けることができます。次に、上記の対応する代替案は次のとおりです。
sed -e ':a' -e '/[|].*[|]/s/[|]/ /;t a' file
Quasímodoのsed
での明示的なループ とは異なるアプローチ:
$ sed 'h; s/.*|//; x; s/|[^|]*$//; y/|/ /; G; y/\n/|/' file
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |406 RCO 301
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
各行について、これはh
を使用してホールドスペースに行を保存し、最後の|
までの行のすべてを削除します。次に、行の元のコピーを入れ替え、最後の|
とその後のすべてを削除します。
これで、パターンスペースにはラインの最初の最初の部分が含まれ、ホールドスペースにはラインの最後の部分が含まれます。
最初のy///
コマンドは、残りのすべての|
をスペースに置き換えます。 G
は、パターンスペースの最後に改行文字を挟んでホールドスペースを追加します。 2番目のy///
コマンドは、その改行文字を|
に変換し、これで完了です。
制限された(固定)数のs///
置換を実行し、可能な場合はより高速なy///
コマンドを使用すると、明示的なループバリエーション(50 MiBデータでは〜2.3秒、最大7.8秒)よりも速く実行されます。 GNU sed
)を使用して、ループのある同じデータで。
興味深いことに、明示的なループのバリエーションで後方参照を使用すると、私とIsaacの両方がそうしたように、さらに遅くなります(〜 Isaacのバリエーション で33秒、そしてmineで〜29秒( コメント内) )、上記と同じデータセットおよび同じ条件で)。
awk
を使用すると、これalmostは、最後の区切り文字を除くすべての|
区切り文字をスペースで置き換えます。 「ほとんど」は、最後の|
の前にスペースを挿入挿入します。
$ awk -F '|' 'BEGIN { OFS = " " } { $NF = "|" $NF; print }' file
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |406 RCO 301
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
|
で区切られたフィールドのセットとして各行を読み取り、最後のフィールドの先頭に|
文字を付加し、フィールド区切り文字用のスペースを含む結果のレコードを出力します。
awk
のデフォルトの動作を考慮に入れて(スペースはデフォルトの出力フィールド区切り文字であり、入力フィールド区切り文字はFS
として使用できます):
awk -F '|' '{ $NF = FS $NF; print }' file
または、少し短く、@ Isaacの厚意により、
awk -F '|' '{ $NF = FS $NF }; 1' file
Perlを使用して、次のように実行できます。
_Perl -pe 's/\|(?=.*\|)/ /g' ex
_
どこ:
Perl -pe
_ action-アクションを実行して出力します\|(?=.*\|)
は、_|
_に一致する正規表現であり、別の_(?=.*|)
_を含む未処理のルックアヘッド_|
_と一致します。Posixly:
_sed -e ':a' -e '/|\(.*|\)/s// \1/;ta' file
_
-e ':a'
_ジャンプするラベル(a
)を定義します。-e '
_別のスクリプトセクションを開始します。/|\(.*|\)/
1つの正規表現は、2つの_|
_に一致し、すべてが真ん中にあり、すべての中間と最後の_|
_をキャプチャします。s// \1/
_上記で一致したすべてをキャプチャされたものに置き換えます。;ta
_ループをもう一度。' file
_。すべてのオプションの速度(高速から低速まで)を測定するには、以下を使用できます。
_#! /bin/bash
TIMEFORMAT='run : %lR sec'
read -d '' str <<\END
FLD1 |SFK TK |FLD2 |FLD4 |FLD5 |- |20200515 |NNNN |406 RCO 301
FLD1 |SFK TK |FLD2 |FLD4 |FLD5 |- |20200515 |NNNN |0
FLD1 |SFK TK |FLD2 |FLD4 |FLD5 |- |20200515 |NNNN |0'
END
n=${1:-100}; printf "$str"'%.0s\n' $(seq "$n") > file
time Perl -pe 's/\|(?=.*\|)/ /g' file >/dev/null
time sed -E ':a;/\|.{1,}\|/s/\|/ /1;ta' file >/dev/null
time sed 'h; s/.*|//; x; s/|[^|]*$//; y/|/ /; G; y/\n/|/' file >/dev/null
time sed 's/\(.*\)|/\1\x00/;y/|/ /;s/\x00/|/1' file >/dev/null
time sed 's/\(.*\)|/\1\n/;y/|/ /;s/\n/|/1' file >/dev/null
_
として使用する:
_$ ./testbash.sh 235000
run : 0m07.676s sec
run : 0m17.753s sec
run : 0m22.074s sec
run : 0m24.036s sec
run : 0m24.047s sec
_
このように不正をするのはどうですか? sed
が1つだけ必要だと言った人はいません:
sed -r 's/\|([^|]+)$/##\1/' | sed 's/|/ /g' | sed 's/##/|/'
ここでは、プレースホルダーとして##
を使用し、行の最後の|
のみをそれで置き換え、他のすべての|
を置き換えてから、最後の行を元に戻しました。入力に##
がないことを確認してください。
使用できる代替案をいくつか紹介します。
$ sed -e '
s/|[^|]*$/\n&/
s/\n|/\n/
y/\n|/| /
' file
$ Perl -pe 's/\|/ / until tr/|/|/ == 1' file
$ Perl -pe 'my $k=tr/|/|/; s/\|/ / while $k-->1' file
フィールドの数が常に同じである場合、cut
+ tr
&paste
をサポートするシェルで<()
:
paste -d\| <(cut -d\| -f1-8 file | tr \| ' ') <(cut -d\| -f9 file)
またはGNU cut
&paste
:
paste -d\| <(cut -d\| --output-delimiter=' ' -f1-8 file) <(cut -d\| -f9 file)
ループなし:
sed 's/\(.*\)|/\1\
/; s/|/ /g; s/\
/|/'