sedの挿入モードを文字列の途中に適用するのを避けるには?

sedの挿入モードを文字列の途中に適用するのを避けるには?

目的

目的は、次の文字列を変換することです。

hello_hello,123-world567-helloworld123456,world1234-hello09876

特定の形式に変換するには、sedを使用してください。

努力する

sed -e 's|^\(hello_[a-z0-9]\{3\}\)\(.*\)|\1,\1\2|g;s|..|&/|g' /tmp/file

予想される結果

he/ll/o_/he/ll/o,123-world567-helloworld123456,/wo/rl/d1/23/4-/he/ll/o0/98/76/

現在の結果

問題は、/2文字ごとにaが挿入されることです。/2 つのカンマ間の挿入を避けてください。

he/ll/o_/he/ll/o,/12/3-/wo/rl/d5/67/-h/el/lo/wo/rl/d1/23/45/6,/wo/rl/d1/23/4-/he/ll/o0/98/76/

答え1

私はこれを行うことができます:

sed 's|\(,[^,]*,\)\{0,1\}\([^,]\{1,2\}\)|\1/\2|g
' <<\IN                                     
hello_hello,123-world567-helloworld123456,world1234-hello09876
IN

...印刷...

/he/ll/o_/he/ll/o,123-world567-helloworld123456,/wo/rl/d1/23/4-/he/ll/o0/98/76

だから最大2番目の代替項目が変更されましたs///。しかし、これは最初の代替項目をすべて削除したためです。

したがって、問題の最大の部分は、2文字ごとに1つずつsed変更するように言うことです/.。点は次のことを意味します。すべての文字そしてgグローバルな意味 - またはみんな

2番目に重要なのは、最初の代替は役に立たず、完全に不要であるということです。

また、最初の交換に追加のカンマを挿入しました。したがって、最初のビットを見つけた後も、まだ追加のフィールドが発生しました。望むより:

\(,[^,]*,\)\{0,1\}\([^,]\{1,2\}\)|\1/\2

これは私にとって適切な代替説明であり、その理由は次のとおりです。

  • \(,[^,]*,\)\{0,1\}- グローバルには気をつけて必要な分だけ受け取らなければなりません。 2文字ごとに置き換えたので、次のような結果が得られますsed貪欲。これを最初に引用することが重要です。なぜならsed、左から右に読み取るとき、通常はカンマではなく2つの連続した文字の間にスラッシュが挿入されるからです。ただし、カンマが見つかった場合は、次のコンマを読み、保存します。\1スラッシュをまったく挿入せずにブロック全体を削除します。

  • \([^,]\{1,2\}\)- ここではドットは使用できません.。カンマと一致するので、区切り文字をスキップしてスラッシュを入力するだけです。コンマを明示的に除外する必要があります。それがすることです - 1つまたは2つのシーケンスごとに -sed常に可能な最大数を取得します。

この例とあなたの例の間で私が見ることができる1つの違いは、ここで最初のスラッシュが文字列の先頭にあり、末尾のスラッシュがないことです。一方、あなたの例では逆です。必要に応じてこの問題を解決するには、次の手順を実行します。

...;s|^/\(.*/.\)/*$|\1/|...

答え2

私は誰かが純粋なアプローチを思い付くと確信していますsedが、この種の作業では、単純な行よりも入力フィールドを理解するプログラムを使用する方がはるかに簡単であることがわかりました。

  1. 真珠

    $ perl -F, -lane 'for($F[0],$F[2]){s|(..)|\1/|g;} print join ",",@F' /tmp/file 
    he/ll/o_/he/ll/o,123-world567-helloworld123456,wo/rl/d1/23/4-/he/ll/o0/98/76/
    

    説明する

    • -a:各入力行をフィールドに分割して@F配列に保存します。最初のdtフィールドは、最初のdtフィールド$F[0]、2番目のフィールド$F[1]などになります。
    • -F:フィールド区切り記号をに設定します,
    • -n-e:各入力行(-n)を読み取ってから提供されるスクリプトを適用します-e
    • -l:末尾の改行を削除し、\n各呼び出しprintにaを追加します。
    • for($F[0],$F[2]){}:最初と3番目のフィールドに適用されます。
    • s|(..)|\1/|g;:簡単な交換で、1文字ずつ/1つずつ追加されます。
    • print join ",",@F':フィールドリストをカンマで連結して印刷します。前のステップでフィールドが変更されたため、変更されたフィールドが印刷されます。
  2. GNU awk

    $ awk -F, -v OFS="," '{$1=gensub(/(..)/,"\\1/","g",$1); $3=gensub(/(..)/,"\\1/","g",$3);}1;' /tmp/file 
    he/ll/o_/he/ll/o,123-world567-helloworld123456,wo/rl/d1/23/4-/he/ll/o0/98/76/
    

    説明する

    上記のように-Fフィールド区切り文字を設定します。-v OFS=","出力区切り記号をに設定します,。その後、gensub()関数(私が信じているGNU awkのみ)は代替操作を実行します。ここでは、最初と3番目のフィールドで動作します。

関連情報