web-dev-qa-db-ja.com

すべての行の最後の文字を除くすべての文字の出現を置き換えます

「|」の出現箇所を置き換えたい[〜#〜] 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
15
et_phonehome
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
18
Quasímodo

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
11
Kusalananda

Perlを使用して、次のように実行できます。

_Perl -pe 's/\|(?=.*\|)/ /g'     ex
_

どこ:

  • _Perl -pe_ action-アクションを実行して出力します
  • \|(?=.*\|)は、_|_に一致する正規表現であり、別の_(?=.*|)_を含む未処理のルックアヘッド_|_と一致します。
9
JJoao

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
_
5
Isaac

このように不正をするのはどうですか? sedが1つだけ必要だと言った人はいません:

sed -r 's/\|([^|]+)$/##\1/' | sed 's/|/ /g' | sed 's/##/|/'

ここでは、プレースホルダーとして##を使用し、行の最後の|のみをそれで置き換え、他のすべての|を置き換えてから、最後の行を元に戻しました。入力に##がないことを確認してください。

2

使用できる代替案をいくつか紹介します。

$ 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
2
Rakesh Sharma

フィールドの数が常に同じである場合、cut + trpasteをサポートするシェルで<()

paste -d\| <(cut -d\| -f1-8 file | tr \| ' ') <(cut -d\| -f9 file)

またはGNU cutpaste

paste -d\| <(cut -d\| --output-delimiter=' ' -f1-8 file) <(cut -d\| -f9 file)
2
user413305

ループなし:

sed 's/\(.*\)|/\1\
/; s/|/ /g; s/\
/|/'
  • 各行の最後の垂直バーを改行に置き換えます。 (これはパターンスペースに挿入される場合がありますが、最初は存在しないことが保証されています。)
  • 残りのすべての垂直バーをスペースに置き換えます。
  • (最初のコマンドで挿入された)改行を垂直バーに置き換えます。