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
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 bycd
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).
[[ -p <(date) ]] && echo true
. Toto vytvořítrue
, když jej spustím s bash 4.4 nebo 3.2.