Substituce procesu a potrubí

Přemýšlel jsem, jak porozumět následujícímu:

Potrubí standardního výstupu příkazu do standardního výkonu jiného je silná technika. Ale co když potřebujete přepsat standardní výstup více příkazů? Tady přichází substituce procesu.

Jinými slovy, může substituce procesu dělat cokoli, co potrubí dokáže?

Co může substituce zpracovat, ale potrubí ne?

Odpovědět

Dobrý způsob, jak si užít Rozdíl mezi nimi je trochu experimentovat na příkazovém řádku. I přes vizuální podobnost při používání znaku <, dělá něco velmi odlišného než přesměrování nebo kanál.

Pojďme použít date příkaz pro testování.

$ date | cat Thu Jul 21 12:39:18 EEST 2011 

Toto je zbytečný příklad, ale ukazuje, že cat přijal výstup date na STDIN a vyplivl jej zpět. Stejných výsledků lze dosáhnout substitucí procesu:

$ cat <(date) Thu Jul 21 12:40:53 EEST 2011 

To, co se právě stalo v zákulisí, se však lišilo. Místo toho, aby dostal stream STDIN, byl cat ve skutečnosti předán název souboru, který je třeba otevřít a přečtěte si. Tento krok můžete zobrazit pomocí echo namísto cat.

$ echo <(date) /proc/self/fd/11 

Když kočka obdržela název souboru, přečetla nám obsah souboru. Na druhou stranu, ozvěna nám právě ukázala název souboru, který byl předán. Tento rozdíl se stane zřetelnějším, pokud přidáte další náhrady:

$ cat <(date) <(date) <(date) Thu Jul 21 12:44:45 EEST 2011 Thu Jul 21 12:44:45 EEST 2011 Thu Jul 21 12:44:45 EEST 2011 $ echo <(date) <(date) <(date) /proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13 

It je možné kombinovat substituci procesu (která generuje soubor) a přesměrování vstupu (které spojuje soubor s STDIN):

$ cat < <(date) Thu Jul 21 12:46:22 EEST 2011 

Vypadá to docela stejně, ale tentokrát byla kočka předána STDIN streamu namísto názvu souboru. Můžete to zkusit pomocí echo:

$ echo < <(date) <blank> 

Protože echo nečte STDIN a nebyl předán žádný argument, nic nedostaneme.

Přesměrování kanálu a vstupu přesouvá obsah do streamu STDIN. Substituce procesu spustí příkazy, uloží jejich výstup do zvláštního dočasného souboru a poté předá tento název souboru namísto příkazu. Ať použijete jakýkoli příkaz, bude se s ním zacházet jako s názvem souboru. Všimněte si, že vytvořený soubor není běžný soubor, ale pojmenovaný kanál, který se automaticky odstraní, jakmile již nebude potřeba.

Komentáře

  • Pokud doopravdy chápáno, tldp.org/LDP/abs/html/process-sub.html#FTN.AEN18244 říká, že substituce procesu vytvoří dočasné soubory, nikoli pojmenované kanály. Pokud vím pojmenovaný, nevytvářejte dočasné soubory. Zápis do kanálu nikdy nezahrnuje zápis na disk: stackoverflow.com/a/6977599/788700
  • Vím, že tato odpověď je legit ‚ protože používá slovo grok : D
  • @Adobe můžete potvrdit, zda náhrada procesu dočasného procesu souboru je pojmenovaná roura s: [[ -p <(date) ]] && echo true. Toto vytvoří true, když jej spustím s bash 4.4 nebo 3.2.

Odpovědět

Tady jsou tři věci, které můžete dělat s nahrazováním procesů, které jsou jinak nemožné.

Více vstupů procesu

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls) 

Tady jednoduše to není možné pomocí potrubí.

Zachování STDIN

Řekněme, že máte následující:

curl -o - http://example.com/script.sh #/bin/bash read LINE echo "You said ${LINE}!" 

A chcete jej spustit přímo. Následující selže bídně. Bash již ke čtení skriptu používá STDIN, takže jiný vstup není možný.

curl -o - http://example.com/script.sh | bash 

Ale tento způsob funguje perfektně.

bash <(curl -o - http://example.com/script.sh) 

Substituce odchozích procesů

Všimněte si také, že substituce procesu funguje i opačně. Můžete tedy udělat něco takového:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \ "/Permission denied/ s/.*\(\/proc.*\):.*/\1/p" > denied.txt ) 

To je trochu spletitý příklad, ale pošle stdout do /dev/null, zatímco pipováním stderr do skriptu sed extrahujete názvy souborů, pro které “ bylo odepřeno oprávnění “ byla zobrazena chyba a poté odešle TYTO výsledky do souboru.

Všimněte si, že první příkaz a přesměrování stdout jsou v závorkách ( subshell ), takže do /dev/null bude odeslán pouze výsledek TOHO příkazu a se zbytkem řádku to nebude dělat potíže.

Komentáře

  • ‚ Stojí za zmínku, že v diff příklad, který by vás mohl zajímat o případ, kdy by cd mohl selhat: diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls).
  • “ zatímco potrubí stderr „: není ‚ poukazují na to, že to není potrubí, ale procházíte souborem fifo?
  • @Gauthier ne; příkaz bude nahrazen nikoli kódem fifo, ale odkazem na deskriptor souboru. “ echo < (echo) “ by tedy mělo přinést něco jako “ / dev / fd / 63 „, což je speciální znakové zařízení, které čte nebo zapisuje z FD číslo 63.

Odpověď

Předpokládám, že mluvíte o bash nebo o nějakém jiném pokročilém prostředí, protože posix prostředí nemá náhradu procesu .

bash manuální přehledy stránek:

Substituce procesu
Substituce procesu je podporována v systémech, které podporují pojmenované kanály (FIFO) nebo metodu / dev / fd pojmenování otevřených souborů. Má formu < (seznam) nebo> (seznam). Seznam procesů je spuštěn s jeho vstupem nebo výstupem připojeným k FIFO nebo nějakému souboru v / dev / fd. Název tohoto souboru je předán jako argument aktuálnímu příkazu jako výsledek rozšíření. Pokud se použije formulář> (seznam), zápis do souboru poskytne vstup do seznamu. Pokud se použije formulář < (seznam), měl by se soubor předaný jako argument číst, aby se získal výstup ze seznamu.

Je-li k dispozici, nahradit proces se provádí současně s rozšiřováním parametrů a proměnných, substitucí příkazů a aritmetickým rozšířením.

Jinými slovy a z praktického hlediska můžete použít výraz jako následující

<(commands) 

jako název souboru pro další příkazy vyžadující soubor jako parametr. Nebo můžete použít přesměrování pro takový soubor:

while read line; do something; done < <(commands) 

Když se vrátím k vaší otázce, zdá se mi, že substituce procesu a potrubí nemají mnoho společného.

Pokud chcete posílat postupně výstup více příkazů, můžete použít jednu z následujících forem:

(command1; command2) | command3 { command1; command2; } | command3 

ale vy lze také použít přesměrování na substituci procesu

command3 < <(command1; command2) 

konečně, pokud command3 přijme parametr souboru (jako náhradu za stdin )

command3 <(command1; command2) 

Komentáře

  • takže < ( ) a < < () má stejný účinek, že?
  • @solfish: not exacllty: the firse can použít všude, kde se očekává název souboru, druhým je přesměrování vstupu pro tento název souboru.

Odpověď

Pokud příkaz bere seznam souborů jako argumenty a zpracovává tyto soubory jako vstup (nebo výstup, ale ne běžně), každý z těchto souborů může být pojmenovaný kanál nebo / dev / fd pseudo soubor poskytovaný transparentně nahrazením procesu:

$ sort -m <(command1) <(command2) <(command3) 

Toto „posílá“ výstup tří příkazů k třídění, protože řazení může trvat seznam vstupních souborů na příkazovém řádku.

Komentáře

  • IIRC je < (příkaz) syntaxe pouze funkcí bash.
  • @Philomath: Je ‚ s Také ZSH.
  • No, ZSH má všechno … (nebo se o to alespoň pokouší).
  • @Philomath: Jak je nahrazování procesů implementováno v jiných skořápkách?
  • @Philomath <(), stejně jako mnoho pokročilých funkcí shellu, byl původně funkcí ksh a byl přijat bash a zsh. psub je konkrétně rybí funkce, s POSIXem nemá nic společného.

Odpověď

Je třeba poznamenat, že substituce procesu není omezena na formu <(command), která používá výstup command jako soubor. Může být ve tvaru >(command), který také posílá soubor jako vstup do command. Toto je také zmíněno v citaci příručky bash v odpovědi @enzotib.

U výše uvedeného příkladu date | cat je to příkaz, který používá procesovou substituci form >(command) k dosažení stejného efektu by bylo,

date > >(cat) 

Všimněte si, že > před >(cat) je nutné. To může být opět jasně ilustrováno echo jako v odpovědi @Caleb.

$ echo >(cat) /dev/fd/63 

Takže bez dalšího > by date >(cat) byl stejné jako date /dev/fd/63 které vytiskne zprávu stderr.

Předpokládejme, že máte program, který jako parametry bere pouze názvy souborů a nezpracovává stdin nebo stdout.Pro ilustraci použiji zjednodušený skript psub.sh. Obsah psub.sh je

#!/bin/bash [ -e "$1" -a -e "$2" ] && awk "{print $1}" "$1" > "$2" 

V zásadě testuje, že oba jeho argumenty jsou soubory (ne nutně pravidelné) soubory) a pokud je to váš případ, napište první pole každého řádku "$1" do "$2" pomocí awk. Pak příkaz, který kombinuje vše, co bylo doposud zmíněno, je:

./psub.sh <(printf "a a\nc c\nb b") >(sort) 

Toto vytiskne

a b c 

a je ekvivalentní

printf "a a\nc c\nb b" | awk "{print $1}" | sort 

, ale následující nebude fungovat a zde musíme použít procesní substituci,

printf "a a\nc c\nb b" | ./psub.sh | sort 

nebo ekvivalentní podoba

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort 

Pokud ./psub.sh také čte stdin kromě toho, co je uvedeno výše, taková ekvivalentní forma neexistuje a v takovém případě není nic, co bychom mohli použít místo náhrady procesu (samozřejmě můžete také použít pojmenovaný kanál nebo dočasný soubor, ale to je jiný příběh).

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *