次のルーチンを考えてみましょう。
alpha() { echo a b c |tr ' ' '\n'; }
paste
ストリームを出力し、出力ストリームをインポートして変換し、元の出力ストリームと一緒に使用したいと思います。
たとえば、upcasingを使用して変換すると、次のように必要なものが得られます。
$ mkfifo p1 p2
$ alpha | tee p1 >( tr a-z A-Z > p2) >/dev/null &
$ paste p1 p2
a A
b B
c C
私の質問はこれを行うより良い方法がありますか?可能であれば、名前付きパイプを含めない方法はありますか?
答え1
ほとんどの unice では、デバイスリンクを介してファイル記述子を参照できます。
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
あなたは非常に奇妙なことが起こっていることを見つけることができます$((_$$))
。これは、できるだけコマンド環境に介入しないようにする方法です。私はここを維持しようと本当に頑張っています。みんな関数の目的は、プロセスの置き換えと同じことを行うことであるため、可能な環境値は私が作成できるほどプリミティブです。パイプに書き込まれた任意のコマンドを実行します。
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
コマンドはすべての引数を単一のコマンドでラップするため、eval
stdout はeval
'd コマンドの全グループの標準出力です。これはシェルこれは、単に標準出力に継承する子プロセスではなく、対応するパイプ記述子を所有します。それでシェルはそうしましたopen()
。なぜなら私たちがそうしたからです。ブロックする open()
(>
代わりにいいです<>
)パイプがリーダーを取得するまで、シェルは停止します。
これはrm
、他のプロセスがパイプの読み取りファイル記述子を設定するまで実行がないことを意味します。一部のプロセスが読み取り用に開くとき(上記のようにcat
)rm
ケーシングが吊り下げられなくなった最初のアクションは、他のアクションを実行する前にパイプが「d」になることです。これはいいえ問題 - パイプラインすでにリーダープロセスとライタープロセスがあります。みんな大丈夫です。
これは少し面倒です。たとえば、
echo "$(_p -)"
/tmp/6023.6023
^それパイプは「d」されませんrm
。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
その他のパイプライン
これを行うには、私のハッキング機能は必要ありません。プロセスの置き換えは非常に一般的です。何もあなたを止めることはできません維持するパイプが必要なときにすぐに割り当てて廃棄する必要はありません。
(編集者注:このファイルがどこにあるのかわからないので、少し嫌いです。~から- おそらくあなたはより適応力のある人なので、次のことを受け入れることができます)
eval "exec 9<> " <(:)
パイプがあります - それはすべてあなたのものです。クリーンアップは必要ありません。今行ってもいいです。
_pp
- へへ
_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
...だから...ついに私と一緒に始めました。血動作することが証明されています。これら[-+]
のオプションは何でも取ることができます。ioe
オプションの意味std(in|out|err)
(stdout
コマンドサブルーチンではほとんど役に立ちませんが)または[0-9]
設定した記述子または任意のファイル名として解釈されるすべてを参照してください。引数を使用して呼び出すと、+
入力が呼び出すコマンドから出る必要があるため、出力ストリームが理解され、引数が-
入力として使用されます。
したがって、sed
上記の最後の内容は次のように書くことができます。
echo hi sed >"$(_pp +2 sed s/sed/mikeserv/)"
hi mikeserv