69095


こんにちは。標準ストリームについての質問です。  
Unixのパイプラインで使用して、透過的に使うツールを作りたいと考えています。(透過的という用語をこの文脈で使っていいのかどうかわかりませんが、具体的には
`tee` のようなものを想定しています。)

下記のようなスクリプト `pass.rb` を作成したとして

[code]

    #! /usr/bin/env ruby
    
    lines = readlines.map(&:chomp)
    
    # do something useful...
    #   
    # end
    
    puts lines
    
[/code]

`chmod +x pass.rb`

この `pass.rb` はパイプの中間に挟んだ時に

[code]

    hoge | ./pass.rb | fuga
    
[/code]

[code]

    hoge | fuga
    
[/code]

fugaに渡される入力データは常に同じになるでしょうか？

もし同等にならない場合は、どのような例で問題が発生するのか、  
どうすれば同等にできるのかを教えて頂けると助かります。

よろしくおねがいします。


CC BY-SA 4.0
2020-07-29T07:51:31.710
69091
2020-08-15T10:22:37.980
32772
question
ruby
Unixのパイプラインで透過的にふるまうRubyのスクリプトを作るには？


`chomp` しているので、入力されたデータの改行コードが変わる可能性があります。

たとえば改行コードのデフォルトが LF の環境において改行コードが CRLF の入力を食わせると、出力の改行コードが LF になってしまいます。

[code]

    % cat foo.txt | od -c
    0000000    a   a   a  \r  \n                                            
    0000005
    % cat foo.txt | ./pass.rb | od -c
    0000000    a   a   a  \n                                                
    0000004
    
[/code]

パイプからテキスト以外のデータが流れてくる可能性も考えると、入出力部分ではバイナリデータとして読み書きし、"do something useful"
部分で必要に応じてテキストデータに変換するのが良さそうです。


CC BY-SA 4.0
2020-07-29T10:15:11.880
69095
2020-08-15T03:41:22.537
2020-08-15T03:41:22.537
19110
19110
69091
answer


> パイプの中間に pass.rb を挟んだ時に  
>  fugaに渡される入力データは常に同じになるでしょうか？  
>  もし同等にならない場合は、どのような例で問題が発生するのか

nekketsuuuさんの回答のとおり、NGとなるケースがあるようです。

> どうすれば同等にできるのかを教えて頂けると助かります。

rubyに詳しくないので、見当違いかもしれませんが、あらゆるケースを想定するなら、バイナリとして読み込み、バイナリとして書き込む方法しか思いつきません。

* * *

次の方法を使えば、お望みのことが実現できると思います。かなり邪道です。  
標準エラー出力を本来とは異なる用途で使っているので、実務に使うのは危険です。  
エラーメッセージがスクリプトに処理されたり、表示されなくなったりするからです。  
なお、teeコマンドとbashのcoprocを使っています。

やり方は以下のとおりです。  
1)coprocでスクリプトを起動します。※例えば`pass.rb`  
2)teeを使って標準入力を標準エラー出力デバイスファイルに流します。  
3)teeの標準出力はcoprocで起動したスクリプトの標準入力に書き込みます。  
4)標準エラー出力を標準出力に戻します。

以下のように実行します。スクリプトはpeep.shとしています。

[code]

    hoge | ./peep.sh スクリプト | fuga
    
[/code]

peep.shの内容

[code]

    #!/bin/bash
    prc=$1
    coproc cprc { ${prc}; }
    {
        tee -p /dev/stderr >&${cprc[1]}
    
    } 2>&1
    
[/code]


CC BY-SA 4.0
2020-07-31T14:52:41.437
69146
2020-07-31T14:52:41.437
35558
69091
answer


次のようにしました。

[code]

    #! /usr/bin/env ruby
    
    while = Kernel.gets(nil)
      input.freeze # 変更されないようにする
    
      # 処理
    
      print input
    end
    
    
[/code]


CC BY-SA 4.0
2020-08-15T10:22:37.980
69579
2020-08-15T10:22:37.980
32772
69091
answer