ShellScript における名前付きパイプによる Goto 文もどき、及び名前付きパイプをループさせる試み

[当方執筆の技術情報に関するライセンス、免責事項、禁止事項](https://qiita.com/EarthSimilarityIndex/items/6b9bb2ccacb38aae15b1)

****

# 執筆動機
思いついたら書かずにはいられなかった。

# （見かけ上の）Goto に類似する挙動

```

$ mkfifo hogepipe; mkfifo fugapipe; mkfifo piyopipe
$ cat hogepipe && echo oyaji &
[1] 31266
$ cat fugapipe > hogepipe &
[2] 31304
$ cat piyopipe > fugapipe &
[3] 31398
$ echo hige > piyopipe
$ hige
oyaji

[1]   Done        cat hogepipe && echo oyaji
[2]-  Done        cat fugapipe > hogepipe
[3]+  Done        cat piyopipe > fugapipe

```

※ 実行順こそシャッフルされていますが、実質一本道です。

# 三行でまとめると
・文字列 "hige" がパイプによって下から上に流れます。
・パイプのつなぎ方次第で次に実行される行を色々と組み替えられそうです。
・シェルの難読化が捗るかも。
・ヒゲといえば配管工兄弟もだけど、手塚治虫作品群。あとマサルさん。

# Goto を待ちながら / Waiting for Goto / En attendant Goto

```

$ mkfifo for; mkfifo while; mkfifo if;
$ cat for > while &
[1]  14187
$ cat while > if &
[2]  14208
$ cat if > for &
[3]  14300
$ jobs
[1]   Running        cat for > while &
[2]   Running        cat while > if &
[3]   Running        cat if > for &
$ Goto () { : ;}
$ killall cat
killall: cat: no process killed
$ exit

```

こちらはいつまで経っても始まらないコードです。
情報の入力がないままに名前付きパイプがループ構造を与えられているためか、CPU使用率が上昇することもありません。~~```cat for > while > if > for &```でも良いのかな？挙動としては同じく始まらないコマンドとなりました。~~ダメっすね。

※ Goto () { :; } は forkbomb ではありません。無害です。

# 何が何だかわからない

~~何故死なない！偽名！？~~

ちょっと舞い上がっていたみたいで大間違いしました。ゴメンなさい。
```cat for > while > if > for &```
↑これではダメですね。ああもうなにやってんだ orz
これでは 次々 1> を切り替えていって最後の for が最終的なリダイレクト先になるだけです。
要は```cat hogepipe > hogepipe &```
怪我の功名というか、これはこれで面白い再帰です。パイプにパイプ自身を繋ぐ最小形かもしれません。

ところで、前節「Goto を待ちながら / Waiting for Goto / En attendant Goto」の形で一度ループさせると、後から echo 等で文字列を突っ込もうとしても無理っぽいです。

あと、殺せないのはプロセス名が cat ではなく bash だからでした。

比較用に sleep の場合。killall で殺せます。

```

esi@slackware:~$ sleep 1000000 &
[1] 2829
esi@slackware:~$ ps aux | grep 2829
esi       2829  0.0  0.0   5124  1616 pts/3    S    18:21   0:00 sleep 1000000
esi       2831  0.0  0.0   9944  1780 pts/3    S+   18:21   0:00 grep 2829
esi@slackware:~$ jobs
[1]+  実行中               sleep 1000000 &
esi@slackware:~$ fg
sleep 1000000
^Z
[1]+  停止                  sleep 1000000
esi@slackware:~$ bg %1
[1]+ sleep 1000000 &
esi@slackware:~$ ps aux | grep 2829
esi       2829  0.0  0.0   5124  1616 pts/3    S    18:21   0:00 sleep 1000000
esi       2833  0.0  0.0   9944  1876 pts/3    S+   18:22   0:00 grep 2829
esi@slackware:~$ jobs
[1]+  実行中               sleep 1000000 &
esi@slackware:~$ killall sleep
esi@slackware:~$ ps aux | grep 2829
esi       2837  0.0  0.0   9944  1872 pts/3    S+   18:23   0:00 grep 2829
[1]+  Terminated              sleep 1000000


```

Godot とか、新世界の神とか、現代怖い。おうちかえる。

# 名前付きパイプのループへのデータの出し入れ（成功編）
コマンドを組み替えたらデータを出し入れ出来るようになりました。

```
$ mkfifo nyarukofifo
$ cat - < nyarukofifo > nyarukofifo &
$ echo '(」・ω・)」うー！' > nyarukofifo
$ echo '(／・ω・)／にゃー！' > nyarukofifo
$ cat nyarukofifo
(／・ω・)／にゃー！
(」・ω・)」うー！
^C
$cat nyarukofifo
^C
$
```

データはぐるぐる回っているので、取り出すタイミングで
(／・ω・)／にゃー！
(」・ω・)」うー！
だったり
(」・ω・)」うー！
(／・ω・)／にゃー！
だったりしました。

加えて、データが空の場合はリソースを殆ど喰いませんが、データを入れるとCPUに負荷がかかり始めます。

また、データは取り出すとループ内から消失する為一回性のメモリとしての使い道があるかもしれません。

追記
ループにデータを加えるとCPUのコア一つが使用率100％になるようです。シングルコアなシステムだと固まるかも。現状のコードではリソースがもったいない。改善の余地有りです。

```nice -n 19 cat - < nyarukofifo > nyarukofifo &```
等としても、データが入っている場合の CPU 使用率は下がりませんね。
cgroups で制限かな？あるいは while や for で無限ループ作って間に sleep 挟むか。

※ 現代怖いので旧支配者に御登場願いました。いあ。

#名前付きパイプループのデータ出し入れ（改良編）
不思議ループのCPU使用率を下げてみる。

## ループの外から介入して CPU 使用率を下げる

```bash
#!/bin/bash
if [ ! -p memorypipe ]; then
mkfifo memorypipe
fi

if ! jobs | grep 'cat - < memorypipe > memorypipe'; then
cat - < memorypipe > memorypipe &
fi

while :
do
kill -STOP $(jobs -p 'cat - < memorypipe > memorypipe')
sleep 1
%'cat - < memorypipe > memorypipe' &
done >/dev/null 2>&1
```

sleep の時間はお好みで。
次はループさせるコマンド側にsleepを組み込みたい。

# 参考
@richmikan@github氏の「mkfifoコマンドって使ってますか？」
https://qiita.com/richmikan@github/items/bb660a58690ac01ec295

bash - How to avoid echo closing FIFO named pipes? - Funny behavior of Unix FIFOs - Stack Overflow
https://stackoverflow.com/questions/8410439/how-to-avoid-echo-closing-fifo-named-pipes-funny-behavior-of-unix-fifos