Tehát egyre keresgélek a megoldások közt, de csak-csak azt látom, hogy "What you cannot do is capture stdout in one variable, and stderr in another, using only FD redirections".
Persze meg lehet trükközni a processz kimeneteinek prefixálásával majd a prefixek alapján való külön válozókba válogatásával. De ez nekem nem igazán jött be.
Aztán találtam egy ötletes megoldást, amivel elkezdtem játszani.
htamas megoldása magasabb programozási szinten operálva hoz letre olyat, amit a bash nem nyújt a szkriptjei számára: kétvégű pipe - mint a pipe(2) rendszerhívás.
Lehet bash-ben file descriptor-okat másolni, lezárni, mind fájlhoz, mind anonymous pipe-hoz, csakhogy épp új pipe-ot nem lehet kérni. Pontosabban olyan új pipe-ot, aminek mindkét vége az irányító scriptből érhető el.
Viszont a jól ismert "|" szintaxissal létrejön egy pipe, csakhogy azon a kötelezően megadandó gyerekprocesszek osztoznak.
Semmi baj, lopjuk el :)
FD másolás:
- dup(2) módra: exec {newfd}>&2 # új fd száma a $newfd változóban
- dup2(2) módra: exec 1>&2 # újonc bashiszták is ismerik
FD lezárás:
- exec 1>&-
- exec {fd}>&- # olvasatomban azonos a következővel
- eval "exec $fd>&-"
Be is csomagoltam ezen kódot egy pipe() nevű bash utility függvénybe, amit
"pipe reader writer" parancssorral hívva egyből kapunk egy használható pipe-ot, és a két végének FD-jeit a $reader és $writer változókban.
Teszteltem, teszteltem, de mintha nem lenne determinisztikus a működése.
Mondom, biztos én vétettem el valamit, amikor külön fgv-be raktam a pipe lezárást, vagy amikor kicseréltem a tail-t a kevésbé költségesnek vélt "sleep inf"-re.
De nem. Egyszer tudok olvasni a $reader-ből, egyszer nem.
Nosza, debuggoljunk! észrevettem, ha az exec elé tettem debug parancsokat, akkor (szinte mindig) jól működött az algoritmus - mi az? Heisenbug?
Nem. Race condition.
Ugyanis "sleep inf | sleep inf &" sorban a bash előbb forkol ki és azután kapcsolja össze egy pipe-on keresztül az egyik processz stdout-ját a másik stdin-jével. Tehát a FD-k módosítása már a jobs, exec és az utána lévő parancsokkal párhuzamos. Így előfordulhat, hogy az exec-cel nem az új FD-ket másolom le a saját processzemnek, hanem a szintén saját stdin-t és stdout-ot.
A lent közölt módosításom ezt a race condition-t oldja fel egy ciklussal.
További szépítés lenne, ha találnék egy soha véget nem érő beépített parancsot a sleep helyére, amivel elkerülhető hogy ennek a parancsnak a hiánya elrontsa a pipe() függvényt.
Javaslom a bash developer csapatnak a sleep builtin implementálását!
Ez az eset is alátámasztja azt a véleményemet, hogy a gyenge hardware serkenti az innovációt:
ezt a race condition-t nehezebben fedeztem volna fel egy gyorsabb gépen, ahol hamarabb, az exec előtt lefutott volna a fork.
pipe()
{
local pid1 pid2 myin myout lnk0 lnk1 wtr rdr
myin=`readlink /proc/self/fd/0`
myout=`readlink /proc/self/fd/1`
sleep inf | sleep inf &
pid2=$!
pid1=$(jobs -p %+)
while true
do
lnk0=`readlink /proc/$pid2/fd/0`
lnk1=`readlink /proc/$pid1/fd/1`
if [ "$lnk0" != "$myin" -a "$lnk1" != "$myout" ]
then
break
fi
done
exec {wtr}>/proc/$pid1/fd/1 {rdr}</proc/$pid2/fd/0
disown $pid2
kill $pid1 $pid2
eval $1=$rdr
eval $2=$wtr
}
close()
{
local fd
for fd in "$@"
do
eval "exec $fd>&-"
done
}
használat:
pipe reader writer
output=`program 2>&$writer`
close $writer
errors=`cat <&$reader`
close $reader
---
Update 16/08/01 - kattinthatalan link jav.
- bAndie9100 blogja
- A hozzászóláshoz be kell jelentkezni
- 765 megtekintés
Hozzászólások
Infinite builtin: while true; do :; ; done
--
Blog | @hron84
Üzemeltető macik
- A hozzászóláshoz be kell jelentkezni
infinite builtin ... ami nem túl CPU-igényes :)
~~~~~~~~
deb http://deb.uucp.hu/ wheezy yazzy repack
- A hozzászóláshoz be kell jelentkezni