`echo`と`printf`では制御文字の扱いが異なる件

# `ffmpeg`で気づいたこと
`ffmpeg`では`-headers`オプションを使えば複数の`http header`を付与しつつ`-i`で指定した`url`に向かって`http`で叩けるが，複数の内容を利用する際には改行するための制御文字を間々で認識させる必要があるらしく，その場合は`$'...'`で表せる．

https://note.kiriukun.com/entry/20201003-to-set-multiple-headers-in-ffmpeg

そしてこの`$'...'`については確かに`zsh`のmanページでも取り扱い方法が記載されていた，以下を参照のこと．

```zsh
man zshmisc | col -b | expand -t 4 | pcre2grep -Moe ' {0}(QUOTING\n)( {7,}(\S* *)*\S*\n*)*'
```

見た感じでは`print`や`printf`は`c++`の実装そのままで，対して`echo`は`zsh`用に作られた安全なものという印象を受けた．




# `zsh`で実験



## 準備
以下のシェルスクリプトを用いる

```zsh:testZsh4
#!/bin/zsh -eu
# this is testZsh4

echo "all arguments : ${*}"

zparseopts -D -E -a optionArray -A optionPair test:

if ((${+optionArray})); then
    for i in $optionArray; do
        echo "echo: component: ${i}"
        printf "printf: %s\n" $i
    done
fi

if ((${+optionPair})); then
    for key in ${(@k)optionPair}; do
        echo "echo: flags: ${key} => value: ${optionPair[$key]}"
        printf "printf: flags: %s => value: %s\n" $key $optionPair[$key]
    done
fi

echo "other arguments : ${*}"
```

`-test`オプションにつける引数を変えることで処理の比較ができる

https://soluna-eureka.hatenablog.com/entry/2022/01/03/074908



## 方針
改行文字を含めた文字列`ab\ncd`を対象に色々と変えてみよう！


### `ab\ncd`

```zsh
% testZsh4 -test ab\ncd
all arguments : -test abncd
echo: component: -test
printf: -test
echo: component: abncd
printf: abncd
echo: flags: -test => value: abncd
printf: flags: -test => value: abncd
other arguments : 
```

両者共に`\`が消えた


###`'ab\ncd'`

```
% testZsh4 -test 'ab\ncd'
all arguments : -test ab
cd
echo: component: -test
printf: -test
echo: component: ab
cd

printf: ab\ncd
echo: flags: -test => value: ab
cd
printf: flags: -test => value: ab\ncd
other arguments : 
```

`echo`だけ改行文字が読まれた，`printf`では文字として扱われた


### `"ab\ncd"`

```zsh
% testZsh4 -test "ab\ncd" 
all arguments : -test ab
cd
echo: component: -test
printf: -test
echo: component: ab
cd
printf: ab\ncd
echo: flags: -test => value: ab
cd
printf: flags: -test => value: ab\ncd
other arguments : 
```

`'ab\ncd'`と変わらない


### `$'ab\ncd'`

``` zsh
% testZsh4 -test $'ab\ncd'
all arguments : -test ab
cd
echo: component: -test
printf: -test
echo: component: ab
cd
printf: ab
cd
echo: flags: -test => value: ab
cd
printf: flags: -test => value: ab
cd
other arguments : 
```

両者とも改行文字が読まれた


### `$"ab\ncd"`

```zsh
% testZsh4 -test $"ab\ncd"
all arguments : -test $ab
cd
echo: component: -test
printf: -test
echo: component: $ab
cd
printf: $ab\ncd
echo: flags: -test => value: $ab
cd
printf: flags: -test => value: $ab\ncd
other arguments : 
```

`"..."`の動作はそのまま先頭に`$`が入っただけだった


## 結論
`printf`をみるに`c++`系の実装なら`$'...'`することでほぼ確実に制御文字を反映させられるっぽい，また`echo`ではそれよりも簡単に制御文字を反映させられるっぽい




# `ffmpeg`での使いかた
`-headers`オプションは多重の呼び出しが不可能である（2回目からは内容が置き換わる）ため，前述の手法で制御文字を有効にすることではじめて複数の内容を利用できる．とは言えその中で頻用される幾つかは`ffmpeg`側でも専用のオプションとして提供されており，本来ならばそういったものを利用するべきではないかとも考えられる（処理がめんどいって？それはそう）．

詳細は以下で確認できる，`-loglevel trace`で動作が全て標準出力される，`-loglevel quiet`で何も表示されない，また`-i [url]`を`-headers [header]`より前に持ってくると`-headers [header]`が認識されない（なぜかドキュメントにはのってないけど）

```zsh
% ffmpeg \
-loglevel trace \
-headers $'Hoge: hoge\r\nHuga: huga' \
-i 'http://example.com/playlist.m3u8' \
-c copy \
output.mp4
...
User-Agent: Lavf/58.76.100
Accept: */*
Range: bytes=0-
Connection: close
Host: example.com
Icy-MetaData: 1
Hoge: hoge
Huga: huga
...
```

以上の結果が得られればヘッダが正しく設定されたと言える（該当の動画は存在しないので最終的には`error`が返される）
本来`\r\n`はms-dosやwindowsで利用される作法であり，旧MacOSでは`\r`のみ・現macOSでは`\n`のみがそれぞれの「標準の改行コード」である，なので`\n`だけで十分ではある…のだが心配なので`\r\n`することを癖にしても良いかもしれない，実際の`\r`はmacOSにおいて「同じ行の先頭まで戻る（そして出力が順に置き換わる）」動作を表すので余計な改行にはならないはず




# おまけ

これ読めばわかると思われるが…？

```zsh:zshmisc > QUOTING
QUOTING
       A character may be quoted (that is, made to stand for itself) by
       preceding it with a `\'.  `\' followed by a newline is ignored.

       A string enclosed between `$'' and `'' is processed the same way as the
       string arguments of the print builtin, and the resulting string is
       considered to be entirely quoted.  A literal `'' character can be
       included in the string by using the `\'' escape.

       All characters enclosed between a pair of single quotes ('') that is not
       preceded by a `$' are quoted.  A single quote cannot appear within single
       quotes unless the option RC_QUOTES is set, in which case a pair of single
       quotes are turned into a single quote.  For example,

          print ''''

       outputs nothing apart from a newline if RC_QUOTES is not set, but one
       single quote if it is set.

       Inside double quotes (""), parameter and command substitution occur, and
       `\' quotes the characters `\', ``', `"', `$', and the first character of
       $histchars (default `!').
```