web-dev-qa-db-ja.com

参加したパイプライン

このようなルーチンを検討します。

alpha() { echo a b c |tr ' ' '\n'; }

ストリームを出力するので、出力ストリームを取得して変換し、元の出力ストリームでpasteしたいと思います。

サンプル変換としてアップケースを使用すると、次の方法で目的を達成できます。

$ mkfifo p1 p2
$ alpha | tee p1 >( tr a-z A-Z > p2) >/dev/null &
$ paste p1 p2
a       A
b       B
c       C

私の質問は、これを行うためのより良い方法、できれば名前付きパイプを含まない方法はありますか?

5
PSkocik

ほとんどのユニスでは、デバイスリンクによってファイル記述子を参照できます。

_alpha(){ echo a b c | tr \  \\n; }
alpha | { alpha |
    tr \[:lower:] \[:upper:] |
    paste - /dev/fd/9
}   9<&0
_

_A   a
B   b
C   c
_

名前付きパイプ


さて、私はすでにこれのかなりうまく機能しているバージョンを持っていました。私のエディターで開いているバージョン(まだ完成していません)は現在機能していません-オプション全体を少しずつ解析して、ぼんやりと覚えているだけです意図。でもこれは...

__p(){   : "$((_$$=0))"
    cd -P -- "${TMPDIR:-/tmp}"          &&
    while   [ -h "$$.$((_$$+=$$))" ]    ||
            [ -e "$$.$((_$$))" ]
    do :;   done                        &&
    mkfifo  -m a-w,u=rwx "$$.$((_$$))"  &&
    printf  %s "$PWD/$$.$((_$$))"       &&
    exec    >&- >&0                     &&
    case    $1 in
    (+)     shift
            eval "  rm $$.$((_$$))
                    cd - >/dev/null
            $@" < "$$.$((_$$))" &;;
    (-)     shift
            eval "  rm $$.$((_$$))
                    cd - >/dev/null
            $@" >   "$$.$((_$$))"   &;;
    (*) set --
        : "${1:?"USAGE: [-+] [cmd args...]"}"
    esac
}   <&- <>/dev/null
_

$((_$$))で起こっている本当に奇妙なことがいくつかあることに気付くでしょう。これは、コマンドの環境をできるだけ踏まないようにするためのハッキーな方法です。この関数のポイントはどのプロセスとほとんど同じことをすることなので、ここではallの可能な環境値をできるだけ元の状態に保つように一生懸命努力しています置換は行います-パイプに書き込む任意のコマンドを実行します。

_cat "$(_p - echo a b c)"
_

_a b c
_

ワークフローの簡略版は次のとおりです。

_mkfifo pipe                         # make a pipe
printf %s\\n "$PWD/pipe"            # substitute its file name
exec <&- </dev/null >&- >/dev/null  # break cmd sub i/o
eval "rm pipe;$@" >pipe &           # eval rm; args
_

evalコマンドは、すべての引数を1つの単純なコマンドにラップするため、evalのstdoutはeval 'dコマンドグループのstdout全体になります。そして、そのパイプ記述子を所有するのはShellであり、単にstdoutとして継承する子プロセスではありません。そして、シェルはopen()を実行します-そして、blockingopen()(_>_ではなく_<>_のように)シェルは、そのパイプがリーダーを取得するまでハングします。

これは、他のプロセスがそのパイプの読み取りファイル記述子を確立するまで、rmが実行されない(実行できない)ことを意味します。あるプロセスがそれを読み取りのために開くと(上記のcatのように)シェルはハングを停止し、最初に実行されるアクションはパイプはrm 'd-何よりも先に。これはnotの問題です-パイプはすでににリーダープロセスがあり、ライタープロセス。すべては順調です。

少しバグがあります。例えば:

_echo "$(_p -)"
_

_/tmp/6023.6023
_

^そのパイプはrm 'dを取得しません。 echoはそれを読みません。したがって、evalはまだ待機しています。それを殺すには:

_: </tmp/6023.6023
_

...しかし、それを殺すには十分です。

このようなものは私がそれを使用するものです:

_exec 9<> "$(_p -)"
sed -ue's/sed/mikeserv/' <&9 &
echo hi sed >&9
_

_hi mikeserv
_

ちなみに、私は<()に相当するものだけを示しましたが、>()は次のようにシミュレートできます...

_echo hi sed >"$(_p + sed -e's/sed/mikeserv/' '>&2')"
_

_hi mikeserv
_

他のパイプ


これには私のハッキー関数は必要ありません。プロセス置換はかなり普及しています。そして、あなたがパイプを要求したときにあなたを止めるものは何もありません維持-あなたはすぐにそれを割り当ててそれを捨てる必要はありません。

(Paranoid note:それらのファイルがどこにあるのかわからないのでちょっと嫌いですから来る-おそらくあなたはよりよく調整された個人なので、次のようになりますあなたに受け入れられます)

_eval "exec 9<> " <(:)
_

パイプがあります-それはすべてあなたのものです。クリーンアップは必要ありません。あなたは行ってもいいです。


__pp_- hehe


__pp(){  :   "$((_$$=0))"
        _e()    case    ${1:-0} in
                (i|0)   exec >&- 1<>/dev/null;;
                (o|1) ! exec <&-  <>/dev/null;;
                (e|[2-9])
                        exec <&-  <>/dev/null >&- >&0
                        set     "${1#e}"
                        return  "${1:-2}";;
                (*[\<\>]*)
                        eval "
                        exec <&-  <>/dev/null >&- >&0 $1"
                esac
        cd -P   -- "${TMPDIR:-/tmp}"            &&
        while   [ -h "$$.$((_$$+=1))" ]         ||
                [ -e "$$.$((_$$))"    ]
        do :;   done                            && 
        mkfifo  -m a-w,u=rwx   "$$.$((_$$))"    &&
        printf  "%s/%s" "$PWD" "$$.$((_$$))"    &&
        case    $1  in
        (+*)    set \> \< "$@"  ;;
        (-*)    set \< \> "$@"  ;;
        (*)     rm  "$$.$((_$$))"
                set ''  USAGE:  '<-+>[+-[fd][file]]'
                ${1:?"$(printf "\n%s\t%s [cmd...]$@")"};;
        esac    &&
        case    $3  in
        (?|?[ioe0-9])
                _e  "${3#?}"
                eval "exec $1&$?";;
        (?/*)   _e "$1"'"$2"' "${3#?}";;
        (*)     _e "$1"'"$2"' "$OLDPWD/${3#?}"
        esac    &&
        eval "  shift 3
                eval \" rm $$.$((_$$))
                        cd - >/dev/null
        \$@\"   $2$$.$((_$$)) &"
}
_

...だから...私はついに__p_ improvedを実行可能にすることに取り掛かりました。 _[-+]_オプションは、ioeオプションのいずれかをstd(in|out|err)(ただし、コマンドsubではstdoutはかなり役に立たない)または_[0-9]_は、すでに設定した記述子、または任意のファイル名として解釈されるその他の記述子を参照します。 _+_で呼び出されると、引数はアウトストリームであると理解されます-入力は、それを呼び出すコマンドから来ると想定されているため-_-_では、引数が入力として使用されます。

したがって、これを使用すると、上記の最後のsedは次のように記述されます。

_echo hi sed >"$(_pp +2 sed s/sed/mikeserv/)"
_

_hi mikeserv
_
6
mikeserv