57539


foo を bar に、 bar を foo に sed で変換したいと思いました。

[code]

    sed -e 's/foo/bar/; t; s/bar/foo/'
    
[/code]

ひとまず、上記を記述してみたのですが、これは実際に実行すると、エラーになります。

[code]

    sh-3.2$ printf 'foo\nbar\n' | sed -e 's/foo/bar/; t; s/bar/foo/'
    sed: 2: "s/foo/bar/; t; s/bar/foo/
    ": undefined label '; s/bar/foo/'
    
[/code]

`man sed` を見てみたところ、以下のような記述があるので、これで動かない理由がわからないです。

[code]

     [2addr]t [label]
             Branch to the ``:'' function bearing the label if any substitutions
             have been made since the most recent reading of an input line or
             execution of a ``t'' function.  If no label is specified, branch to
             the end of the script.
    
[/code]

### 質問

どうして、この `t` function は動かないのでしょうか？

### 環境

  * macOS 10.14.6
  * `man sed` の結果: `May 10, 2005` の BSD のものである、と書いてある。


CC BY-SA 4.0
2019-08-22T05:56:38.237
57533
2019-08-22T09:35:28.243
2019-08-22T07:08:30.040
3060
754
question
macos sed
mac の sed で t コマンドがうまく動かない


`sed` の `t` はあまり使わないコマンドなので（オイラも初めて使った）読者のために解説を入れておきます。

`t LABEL`  
行読み込み成功＋直近の `t` 以後に `s` が成功していたなら `LABEL` に分岐する  
`LABEL` が省略されているとき、スクリプトの末尾に分岐する

エラーメッセージを素直に読むに、標準 `sed` ではセミコロンを含めて `t`
コマンドに与えるべきラベルと解釈されているようです。おそらく「セミコロンでコマンドを区切る」ことが `GNU sed` の拡張なのでしょう。

ウチの `hppa2.0w-hp-hpux11.11` の標準 `sed` では

[code]

    $ which sed
    /usr/bin/sed
    $ echo foobarbaz | sed -e's/foo/bar/; t; s/bar/poo/;'
    sed: The label s/foo/bar/; t; s/baz/poo/; is greater than eight characters.
    $ echo foobarbaz | sed -e's/foo/bar/' -e't' -e's/baz/poo/'
    barbarbaz
    $ echo foxbarbaz | sed -e's/foo/bar/' -e't' -e's/baz/poo/'
    foxbarpoo
    $ echo foobarbaz | sed -e's/foo/bar/' -e's/baz/poo/'
    barbarpoo
    $
    
[/code]

ということで、セミコロンでコマンドを区切るのではなく `-e` を複数回（この例では３回）指定すれば希望通りになりそう。


CC BY-SA 4.0
2019-08-22T07:05:42.017
57535
2019-08-22T07:05:42.017
8589
57533
answer


Mac OSX の sed コマンドは FreeBSD 由来(もしくは NetBSD)らしいので、そちらのソースコードを眺めてみました。

**freebsd/usr.bin/sed/compile.c**

[code]

    static struct s_format cmd_fmts[] = {
            :
      {'s', 2, SUBST},
            :
      {'t', 2, BRANCH},
            :
    };
    
    static struct s_command **
    compile_stream(struct s_command **link)
    {
               :
      case BRANCH:      /* b t */
        p++;
        EATSPACE();
        if (*p == '\0')
          cmd->t = NULL;
        else
          cmd->t = duptoeol(p, "branch");
        break;
    
    /*
     * duptoeol --
     *  Return a copy of all the characters up to \n or \0.
     */
    static char *
    duptoeol(char *s, const char *ctype)
    
[/code]

`t` コマンドの場合、`duptoeol()` 関数でラベル文字列を読み取っています(`t`
直後の空白文字はスキップ)。そのため、以下の様にすると想定通りに動作します。

[code]

    $ printf 'foo\nbar\n' |
      sed -e '
        s/foo/bar/
        t
        s/bar/foo/
      '
    bar
    foo
    
    $ printf 'foo\nbar\n' | sed -e 's/foo/bar/;t' -e 's/bar/foo/'
    bar
    foo
    
[/code]

bash や zsh の場合は `$''` 記法を使って以下の様に書く事もできます。

[code]

    $ printf 'foo\nbar\n' | sed -e $'s/foo/bar/;t\ns/bar/foo/'
    bar
    foo
    
[/code]

なお、`s` コマンドでは `;`(セミコロン)をコマンド文字列の終端として処理します。

**freebsd/usr.bin/sed/compile.c**

[code]

    case SUBST:      /* s */
      p++;
            :
      EATSPACE();
      if (*p == ';') {
        p++;
        link = &cmd->next;
        goto semicolon;
      }
      break;
    
[/code]

参考までに、[sed で、セミコロンを使うとおかしくなる](https://ja.stackoverflow.com/questions/28155)
を挙げておきます(こちらは GNU sed のお話です)。


CC BY-SA 4.0
2019-08-22T08:46:43.633
57539
2019-08-22T08:46:43.633
57533
answer


<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html>

^ 上記、 posix の sed の仕様を眺めていました。

> Editing commands other than **{...}** , **a** , **b** , **c** , **i** ,
> **r** , **t** , **w** , **:** , and **#** can be followed by a
> `<semicolon>`, optional `<blank>` characters, and another editing command.
> However, when an **s** editing command is used with the **w** flag,
> following it with another command in this manner produces undefined results.

なので、端的に言うと、`;` セミコロンによってコマンドを終了できると明示されているのは、「複合コマンドである **{...}**
であったり、コマンドそれ自体が末尾に可変長名称を取る場合」 **以外**
であり、可変っぽい末尾に対しては、セミコロンは使わないのが無難である、という結論をえました。


CC BY-SA 4.0
2019-08-22T09:35:28.243
57542
2019-08-22T09:35:28.243
754
57533
answer