ラベル

:(コロン)コマンドでsedスクリプトの任意の位置にラベル(名前)を付けて、別の場所からb,t,Tコマンドでラベルの位置にジャンプ(分岐)できる。


    ラベルの操作コマンド


    ラベルの基本操作

    まず、:コマンドはアドレスが付けられず、付けるとエラー。

    echo | sed '1 : label'
    sed: -e expression #1, char 3: : にアドレスは不要です
    

    ただし、ブロック({コマンド)で囲んだ中に入れることで間接的にアドレスの機能を利用できる。

    下記は普通なら到達できないブロック(ラベルの位置)にbコマンドでジャンプする例。ラベル名は日本語も可。

    input='#
    a
    b'
    
    script='
    1,$ ! { # どの行にも該当しないので、普通なら到達できないブロック
      : ラベル
      d
    }
    b ラベル
    '
    
    echo "$input" | sed "$script"
    

    上記は、結局すべての行にdコマンドが適用されるため、出力は無し。なお、どの行にも該当しないアドレスはアドレス否定の!だけでも機能し、下記はエラーにならない。

    echo | sed '! : label'

    また、下記のようにスクリプトの後方にラベルが位置していても前方からジャンプできる。

    seq 1 3 | sed 'b jump; d; : jump;'

    上記は飛び越えたdコマンドが実行されないので、全出力。

    1
    2
    3

    そして、ラベル名を付けないbコマンドは、スクリプト末尾に(次のサイクルへ)移動するので、下記も全出力となる。

    seq 1 3 | sed 'b; d;'

    ラベルをループ処理に使う

    下記は「a」の行から最後まで改行を取り除く例。このようにループ処理ができるラベルの注意点として、ループから抜ける手順が無いと無限ループになってしまうこと。

    input='#
    a
    b
    c
    '
    
    script='
    /a/ {
      : ループ
      # 次行をパターンスペースに追加、最終行に来たら終了
      N
      # 改行削除
      s/\n//
      b ループ
    }
    '
    
    echo "$input" | sed "$script"
    

    出力はこうなる。

    #
    abc

    ループから抜ける手段

    ループから抜けるためには、ラベルの他、dコマンドDコマンドqコマンドQコマンドnコマンドNコマンドなどが使える。


    tコマンド

    tコマンドは、sコマンド成功条件付きジャンプ。置換が実施されたら何かしたい/したくない時に使える。

    input='# 
    a
    b
    c
    '
    
    script='
    s/a/A/
    t # sコマンドが成功したら末尾(次のサイクル)へジャンプ
    d
    '
    
    echo "$input" | sed "$script"
    

    「a」の行だけsコマンドが成功しdコマンドが飛ばされるので、出力は「A」だけとなる。


    Tコマンド

    下記は複数行を通して一つ目の漢字だけにルビを振る例。

    input='# 
    漢字 漢字
    漢字
    漢字な漢字'
    
    script='
    s/漢字/漢字(かんじ)/
    T # 置換がなければ末尾(次のサイクル)へジャンプ
    : ループ
    n
    b ループ
    '
    
    echo "$input" | sed "$script"
    
    #
    漢字(かんじ) 漢字
    漢字
    漢字な漢字
    

    特定のセクションをすべて抽出する

    下記のようにセクションで区切られた内容から、特定セクションを複数あればすべて抽出したい場合。

    # セクションXがいくつかある
    セクションX
    x1
    セクションa
    a
    セクションX
    ...
    

    これをアドレス2個の正規表現で/セクションX/,/セクション/のようにやると、次のセクションの開始行まで範囲に含まれてしまい、難しい。

    下記は、ターゲットの「セクションX」を検出したらそれが終わるまでループして抽出する例。

    input='# すべてのセクションXを抽出したい
    セクションX
    x1
    セクションa
    a
    セクションX
    セクションb
    b
    セクションX
    x2
    x3
    セクションX
    x4
    '
    
    target='^セクションX'
    delimiter='^セクション' # セクションの区切り
    
    script="
    /$target/ {
      : ループ
      /$delimiter/ {
        /$target/ ! d # セクション終了でループを抜ける
      }
      p
      n
      b ループ
    }
    "
    
    echo "$input" | sed --regexp-extended --quiet "$script"
    

    出力はこうなる。

    セクションX
    x1
    セクションX
    セクションX
    x2
    x3
    セクションX
    x4