GNU/Linux >> Znalost Linux >  >> Linux

Jak používat příkaz Coproc v různých skořápkách?

Může někdo poskytnout pár příkladů, jak používat coproc ?

Přijatá odpověď:

koprocesy jsou ksh funkce (již v ksh88 ). zsh má tuto funkci od začátku (začátek 90. ​​let), zatímco byla právě přidána do bash v 4.0 (2009).

Nicméně chování a rozhraní se mezi těmito 3 shelly výrazně liší.

Myšlenka je však stejná:umožňuje spuštění úlohy na pozadí a možnost posílat jí vstup a číst její výstup, aniž byste se museli uchýlit k pojmenovaným kanálům.

To se provádí pomocí nepojmenovaných kanálů s většinou shellů a párů zásuvek s nejnovějšími verzemi ksh93 na některých systémech.

V a | cmd | b , a odešle data do cmd a b přečte jeho výstup. Spuštění cmd jako koproces umožňuje shell být jak a a b .

spoluprocesy ksh

V ksh , spustíte koproces jako:

cmd |&

Data odešlete do cmd děláním věcí jako:

echo test >&p

nebo

print -p test

A přečtěte si cmd výstup s věcmi jako:

read var <&p

nebo

read -p var

cmd se spouští jako jakákoli úloha na pozadí, můžete použít fg , bg , kill na něj a odkazujte jej pomocí %job-number nebo prostřednictvím $! .

Pro uzavření zapisovacího konce roury cmd čte z, můžete:

exec 3>&p 3>&-

A k uzavření čtecího konce druhého kanálu (ten cmd píše):

exec 3<&p 3<&-

Nemůžete spustit druhý společný proces, pokud nejprve neuložíte deskriptory souboru roura do jiných fds. Například:

tr a b |&
exec 3>&p 4<&p
tr b c |&
echo aaa >&3
echo bbb >&p

zsh spoluzpracovává

V zsh , koprocesy jsou téměř totožné s těmi v ksh . Jediný skutečný rozdíl je v tom, že zsh koprocesy se spouští pomocí coproc klíčové slovo.

coproc cmd
echo test >&p
read var <&p
print -p test
read -p var

Dělám:

exec 3>&p

Poznámka:Toto nepřesune coproc deskriptor souboru na fd 3 (jako v ksh ), ale duplikuje to. Neexistuje tedy žádný explicitní způsob, jak uzavřít přívodní nebo čtecí potrubí, jiný začíná jiným coproc .

Chcete-li například zavřít konec podávání:

coproc tr a b
echo aaaa >&p # send some data

exec 4<&p     # preserve the reading end on fd 4
coproc :      # start a new short-lived coproc (runs the null command)

cat <&4       # read the output of the first coproc

Kromě společných procesů založených na potrubí, zsh (od 3.1.6-dev19, vydané v roce 2000) má konstrukty založené na pseudo-tty jako expect . Pro interakci s většinou programů nebudou koprocesy ve stylu ksh fungovat, protože programy se začnou ukládat do vyrovnávací paměti, když je jejich výstup roura.

Zde je několik příkladů.

Spusťte společný proces x :

zmodload zsh/zpty
zpty x cmd

(Zde cmd je jednoduchý příkaz. Ale s eval můžete dělat mnohem lepší věci nebo funkce.)

Zasílejte údaje o společném zpracování:

zpty -w x some data

Čtení údajů o společném zpracování (v nejjednodušším případě):

zpty -r x var

Jako expect , může čekat na nějaký výstup ze společného procesu odpovídající danému vzoru.

spoluprocesy bash

Syntaxe bash je mnohem novější a staví na nové funkci nedávno přidané do ksh93, bash a zsh. Poskytuje syntaxi umožňující manipulaci s dynamicky alokovanými deskriptory souborů nad 10.

bash nabízí základní coproc syntaxe a rozšířená jeden.

Základní syntaxe

Základní syntaxe pro zahájení společného procesu vypadá jako zsh 's:

coproc cmd

V ksh nebo zsh , kanály do az společného procesu jsou přístupné pomocí >&p a <&p .

Ale v bash , deskriptory souboru kanálu ze společného procesu a druhého kanálu do společného procesu jsou vráceny v $COPROC pole (respektive ${COPROC[0]} a ${COPROC[1]} . Takže…

Předat data do společného procesu:

echo xxx >&"${COPROC[1]}"

Čtení dat ze společného procesu:

read var <&"${COPROC[0]}"

Se základní syntaxí můžete v daném okamžiku spustit pouze jeden společný proces.

Rozšířená syntaxe

V rozšířené syntaxi můžete pojmenovat vaše společné procesy (jako v zsh zpty co-processes):

coproc mycoproc { cmd; }

Příkaz has být složeným příkazem. (Všimněte si, jak výše uvedený příklad připomíná function f { ...; } .)

Tentokrát jsou deskriptory souborů v ${mycoproc[0]} a ${mycoproc[1]} .

Můžete spustit více než jeden společný proces najednou – ale to uděláte zobrazí se upozornění, když spustíte společný proces, zatímco jeden stále běží (i v neinteraktivním režimu).

Při použití rozšířené syntaxe můžete deskriptory souborů zavřít.

coproc tr { tr a b; }
echo aaa >&"${tr[1]}"

exec {tr[1]}>&-

cat <&"${tr[0]}"

Všimněte si, že zavírání tímto způsobem nefunguje ve verzích bash starších než 4.3, kde to musíte místo toho napsat:

fd=${tr[1]}
exec {fd}>&-

Stejně jako v ksh a zsh , tyto popisovače souborů kanálu jsou označeny jako close-on-exec.

Ale v bash , jediný způsob, jak je předat provedeným příkazům, je duplikovat je do fds , 1 , nebo 2 . To omezuje počet společných procesů, se kterými můžete pracovat pro jeden příkaz. (Příklad viz níže.)

Související:Spouštět nedůvěryhodné aplikace bezpečně pomocí příkazu sandbox-exec?

yash proces a přesměrování potrubí

yash nemá samo o sobě funkci co-process, ale stejný koncept lze implementovat pomocí jeho potrubí a zpracovat funkce přesměrování. yash má rozhraní k pipe() systémové volání, takže takové věci lze relativně snadno provést ručně.

Zahájili byste společný proces s:

exec 5>>|4 3>(cmd >&5 4<&- 5>&-) 5>&-

Který nejprve vytvoří pipe(4,5) (5 konec zápisu, 4 konec čtení), pak přesměruje fd 3 do roury na proces, který běží se svým stdin na druhém konci, a stdout přejde na rouru vytvořenou dříve. Poté zavřeme konec pro psaní této roury v nadřazeném prvku, který nebudeme potřebovat. Takže nyní v shellu máme fd 3 připojený k stdin cmd a fd 4 připojený k stdout cmd pomocí potrubí.

Všimněte si, že u těchto deskriptorů souborů není nastaven příznak close-on-exec.

Zdroj dat:

echo data >&3 4<&-

Čtení dat:

read var <&4 3>&-

A můžete fds zavřít jako obvykle:

exec 3>&- 4<&-

Proč nejsou tak populární

téměř žádná výhoda oproti používání pojmenovaných kanálů

Společné procesy lze snadno implementovat pomocí standardních pojmenovaných kanálů. Nevím, kdy byly přesně pojmenované roury představeny, ale je možné, že to bylo po ksh přišel se společnými procesy (pravděpodobně v polovině 80. let byl ksh88 „vydán“ v roce 88, ale věřím, že ksh byl interně používán v AT&T několik let před tím), což by vysvětlovalo proč.

cmd |&
echo data >&p
read var <&p

Lze psát pomocí:

mkfifo in out

cmd <in >out &
exec 3> in 4< out
echo data >&3
read var <&4

Interakce s nimi je jednodušší – zvláště pokud potřebujete spustit více než jeden společný proces. (Viz příklady níže.)

Jediná výhoda použití coproc spočívá v tom, že po použití nemusíte tyto pojmenované trubky čistit.

náchylný k uváznutí

Shelly používají potrubí v několika konstrukcích:

  • shell pipes: cmd1 | cmd2 ,
  • záměna příkazů: $(cmd) ,
  • a zpracovat náhradu: <(cmd) , >(cmd) .

V těch tečou data pouze v jednom směr mezi různými procesy.

Díky společným procesům a pojmenovaným kanálům je však snadné se dostat do slepé uličky. Musíte sledovat, který příkaz má který deskriptor souboru otevřený, abyste předešli tomu, že jeden zůstane otevřený a udrží proces při životě. Vyšetřování uváznutí může být složité, protože k nim může docházet nedeterministicky; například pouze v případě, že je odesláno tolik dat, aby se zaplnilo jedno potrubí.

funguje hůř, než expect pro to, k čemu byl navržen

Hlavním účelem koprocesů bylo poskytnout shellu způsob interakce s příkazy. Nicméně to nefunguje tak dobře.

Nejjednodušší forma uváznutí zmíněná výše je:

tr a b |&
echo a >&p
read var<&p

Protože jeho výstup nejde do terminálu, tr vyrovnává svůj výstup. Takže nevypíše nic, dokud buď neuvidí konec souboru na svém stdin nebo nashromáždil vyrovnávací paměť plnou dat pro výstup. Takže výše, poté, co shell vypíše an (pouze 2 bajty), read bude blokovat na dobu neurčitou, protože tr čeká, až mu shell pošle další data.

Stručně řečeno, roury nejsou dobré pro interakci s příkazy. Společné procesy lze použít pouze k interakci s příkazy, které neukládají svůj výstup do vyrovnávací paměti, nebo příkazy, kterým lze říci, aby svůj výstup nevyrovnávaly; například pomocí stdbuf s některými příkazy na nejnovějších systémech GNU nebo FreeBSD.

Proto expect nebo zpty místo toho použijte pseudoterminály. expect je nástroj určený pro interakci s příkazy a dělá to dobře.

Manipulace s deskriptorem souboru je nešikovná a je těžké ji správně nastavit

Společné procesy lze použít k provádění složitějších instalatérských prací, než jaké umožňují jednoduché skořepinové trubky.

že další odpověď Unix.SE má příklad použití coproc.

Zjednodušený příklad: Představte si, že chcete funkci, která předá kopii výstupu příkazu 3 dalším příkazům, a poté necháte výstup těchto 3 příkazů zřetězit.

Vše pomocí potrubí.

Například:vložte výstup printf '%sn' foo bar na tr a b , sed 's/./&&/g' a cut -b2- získat něco jako:

foo
bbr
ffoooo
bbaarr
oo
ar

Za prvé, není to nutně zřejmé, ale existuje možnost uváznutí, které se začne dít již po několika kilobajtech dat.

Potom, v závislosti na vašem shellu, narazíte na řadu různých problémů, které je třeba řešit odlišně.

Související:Jak je zástupný znak * interpretován jako příkaz?

Například pomocí zsh , udělali byste to pomocí:

f() (
  coproc tr a b
  exec {o1}<&p {i1}>&p
  coproc sed 's/./&&/g' {i1}>&- {o1}<&-
  exec {o2}<&p {i2}>&p
  coproc cut -c2- {i1}>&- {o1}<&- {i2}>&- {o2}<&-
  tee /dev/fd/$i1 /dev/fd/$i2 >&p {o1}<&- {o2}<&- &
  exec cat /dev/fd/$o1 /dev/fd/$o2 - <&p {i1}>&- {i2}>&-
)
printf '%sn' foo bar | f

Výše uvedené koprocesní fds mají nastavený příznak close-on-exec, ale ne ty, které jsou z nich duplikovány (jako v {o1}<&p ). Abyste se vyhnuli uváznutí, musíte se ujistit, že jsou uzavřeny ve všech procesech, které je nepotřebují.

Podobně musíme použít subshell a použít exec cat na konci, aby se zajistilo, že žádný skořápkový proces nebude lhát o držení potrubí otevřené.

Pomocí ksh (zde ksh93 ), to by muselo být:

f() (
  tr a b |&
  exec {o1}<&p {i1}>&p
  sed 's/./&&/g' |&
  exec {o2}<&p {i2}>&p
  cut -c2- |&
  exec {o3}<&p {i3}>&p
  eval 'tee "/dev/fd/$i1" "/dev/fd/$i2"' >&"$i3" {i1}>&"$i1" {i2}>&"$i2" &
  eval 'exec cat "/dev/fd/$o1" "/dev/fd/$o2" -' <&"$o3" {o1}<&"$o1" {o2}<&"$o2"
)
printf '%sn' foo bar | f

(Poznámka: To nebude fungovat na systémech, kde je ksh používá socketpairs místo pipes a kde /dev/fd/n funguje jako na Linuxu.)

V ksh , fds nad 2 jsou označeny příznakem close-on-exec, pokud nejsou předány explicitně na příkazovém řádku. Proto nemusíme zavírat nepoužívané deskriptory souborů jako u zsh —ale to je také důvod, proč musíme udělat {i1}>&$i1 a použijte eval pro tuto novou hodnotu $i1 , které budou předány tee a cat

V bash to nelze provést, protože se nemůžete vyhnout příznaku close-on-exec.

Výše je to relativně jednoduché, protože používáme pouze jednoduché externí příkazy. Je to složitější, když tam místo toho chcete použít konstrukce shellu a začnete narážet na chyby v shellu.

Porovnejte výše uvedené se stejným pomocí pojmenovaných kanálů:

f() {
  mkfifo p{i,o}{1,2,3}
  tr a b < pi1 > po1 &
  sed 's/./&&/g' < pi2 > po2 &
  cut -c2- < pi3 > po3 &

  tee pi{1,2} > pi3 &
  cat po{1,2,3}
  rm -f p{i,o}{1,2,3}
}
printf '%sn' foo bar | f

Závěr

Pokud chcete pracovat s příkazem, použijte expect , nebo zsh 's zpty nebo pojmenované kanály.

Chcete-li provést nějaké efektní instalatérské práce s potrubím, použijte pojmenované potrubí.

Co-process může provést některé z výše uvedených, ale buďte připraveni udělat vážné škrábání hlavou pro cokoli netriviálního.


Linux
  1. Jak používat příkaz sed pro Linux

  2. Jak používat Linuxový příkaz grep

  3. Jak používat příkaz historie v Linuxu

  1. Jak používat příkaz basename?

  2. Jak používat příkaz id v Linuxu

  3. Jak používat příkaz „screen“ v Linuxu

  1. Jak používat příkaz vmstat

  2. Jak používat příkaz historie Linuxu

  3. Jak používat příkaz Bash read