88849


以下シェルスクリプトがあるとします。

[code]

    for i in `seq 5`
    do
      echo ${i} # cmd1
    done | cat # cmd2
    
[/code]

このとき、パイプでつながれたcatコマンドはforのループが終了してから起動されるのでしょうか？  
それとも、ループと並行して起動し、echoの出力が都度送り込まれるような動きになるのでしょうか？


CC BY-SA 4.0
2022-05-16T07:57:26.970
88847
2022-05-16T23:46:05.087
2022-05-16T08:23:47.687
3060
17238
question
bash shellscript
シェルスクリプトでfor文のコマンド出力をパイプでつないだ時のコマンド実行順序


試しに上記`shellscript`を以下のように改造してみました

[code]

    #/bin/sh
    for i in `seq 5`
    do
      echo ${i} # cmd1
    done | echo $$ #cmd2
    
[/code]

結果はプロセス番号が1行しか表示されなかったので、`for`のループが終了してからパイプが起動されると思われます。


CC BY-SA 4.0
2022-05-16T08:15:10.600
88848
2022-05-16T08:15:10.600
14745
88847
answer


スクリプト名を `test.sh` として、strace コマンドで処理を追跡してみます。これを見て考えてみて下さい。

[code]

    $ strace -tt -f -e dup2,execve,write ./test.sh
    17:59:49.438629 execve("./test.sh", ["./test.sh"], 0x7ffddf147b30 /* 81 vars */) = 0
    17:59:49.440768 dup2(3, 255)            = 255
    strace: Process 145782 attached
    strace: Process 145783 attached
    [pid 145783] 17:59:49.441452 dup2(3, 0) = 0
    [pid 145782] 17:59:49.441493 dup2(4, 1) = 1
    strace: Process 145784 attached
    [pid 145784] 17:59:49.441858 dup2(4, 1) = 1
    [pid 145783] 17:59:49.442112 execve("/usr/bin/cat", ["cat"], 0x560ddec53970 /* 81 vars */) = 0
    [pid 145784] 17:59:49.442490 execve("/usr/bin/seq", ["seq", "5"], 0x560ddec53970 /* 81 vars */) = 0
    [pid 145784] 17:59:49.443350 write(1, "1\n2\n3\n4\n5\n", 10) = 10
    [pid 145784] 17:59:49.443487 +++ exited with 0 +++
    [pid 145782] 17:59:49.443526 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=145784, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
    [pid 145782] 17:59:49.443638 write(1, "1\n", 2) = 2
    [pid 145783] 17:59:49.443667 write(1, "1\n", 21
     <unfinished ...>
    [pid 145782] 17:59:49.443684 write(1, "2\n", 2 <unfinished ...>
    [pid 145783] 17:59:49.443701 <... write resumed>) = 2
    [pid 145782] 17:59:49.443709 <... write resumed>) = 2
    [pid 145782] 17:59:49.443729 write(1, "3\n", 2 <unfinished ...>
    [pid 145783] 17:59:49.443742 write(1, "2\n", 22
     <unfinished ...>
    [pid 145782] 17:59:49.443754 <... write resumed>) = 2
    [pid 145783] 17:59:49.443775 <... write resumed>) = 2
    [pid 145782] 17:59:49.443808 write(1, "4\n", 2) = 2
    [pid 145782] 17:59:49.443870 write(1, "5\n", 2 <unfinished ...>
    [pid 145783] 17:59:49.443891 write(1, "3\n4\n", 4 <unfinished ...>
    3
    4
    [pid 145782] 17:59:49.443902 <... write resumed>) = 2
    [pid 145783] 17:59:49.443911 <... write resumed>) = 4
    [pid 145783] 17:59:49.443937 write(1, "5\n", 25
    ) = 2
    [pid 145782] 17:59:49.444017 +++ exited with 0 +++
    [pid 145783] 17:59:49.444158 +++ exited with 0 +++
    17:59:49.444622 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=145782, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
    17:59:49.444817 +++ exited with 0 +++
    
[/code]


CC BY-SA 4.0
2022-05-16T08:51:46.207
88849
2022-05-16T09:03:47.503
2022-05-16T09:03:47.503
47127
47127
88847
answer


パイプの継続のコマンドによって ループは中断されることもあります。  
(以下のような場合に, 該当するファイルが見つかればその後は必要ない, などのとき)

[code]

    $ for f in ~/data/*; do echo $f; done | ファイル選択処理
    
[/code]

例えばループの数値が大きくても, 後続のコマンドが一瞬で終了する場合, ループもすぐに終了するでしょう。同時に動いてないと無理です

[code]

    $ time for i in `seq 600000`; do echo ${i}; done >/dev/null 
    
    real    0m2.910s
    user    0m2.657s
    sys 0m0.280s
    
    $ time for i in `seq 600000`; do echo ${i}; done | python -V
    Python 3.10.4
    
    real    0m0.349s
    user    0m0.284s
    sys 0m0.117s
    
[/code]


CC BY-SA 4.0
2022-05-16T09:15:09.307
88851
2022-05-16T09:45:57.893
2022-05-16T09:45:57.893
43025
43025
88847
answer


> このとき、パイプでつながれたcatコマンドはforのループが終了してから起動されるのでしょうか？  
>  それとも、ループと並行して起動し、echoの出力が都度送り込まれるような動きになるのでしょうか？

パイプでつながれたcmd2はforのループの終了をまたずに起動されます。

質問の例のスクリプトはcmd1がすぐに終了するので、cmd1とcmd2が並行するのを確認できません。

すぐに終了しないコマンドをcmd1とした場合、cmd1とcmd2が並行することを確認できます。  
cmd1：yesコマンド  
cmd2：lessコマンド

[code]

    for i in `seq 5`
    do
        yes # cmd1
    done | less # cmd2
    
[/code]

この例ではcmd1がずっとyを標準出力に書き続けますが、cmd2はforのループが終了をまたずに標準入力から受け取ったデータを表示(標準出力)します。


CC BY-SA 4.0
2022-05-16T23:46:05.087
88865
2022-05-16T23:46:05.087
35558
88847
answer