it-swarm-eu.dev

Náhrada procesu a potrubí

Zajímalo by mě, jak porozumět následujícímu:

Potlačení stdout příkazu do stdin jiného je mocnou technikou. Ale co když budete potřebovat potrubí pro více příkazů? Zde přichází substituce procesu.

Jinými slovy, může proces substituce dělat cokoli potrubí?

Co může substituce procesu udělat, ale potrubí nemůže?

89
Tim

Dobrým způsobem, jak rozeznat rozdíl mezi nimi, je udělat trochu experimentování na příkazovém řádku. Navzdory vizuální podobnosti při použití < znak, to dělá něco velmi jiného, ​​než přesměrování nebo potrubí.

Pro testování použijeme příkaz date.

$ 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 ho 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 za scénami, však bylo jiné. Místo toho, aby byl dán proud STDIN, byl cat ve skutečnosti předán název souboru, který je třeba otevřít a přečíst. Tento krok můžete vidět pomocí echo místo cat.

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

Když kočka dostala název souboru, přečetla pro nás obsah souboru. Na druhou stranu nám echo právě ukázala název souboru, že byl předán. Tento rozdíl se stane viditelnějším, pokud přidáte další substituce:

$ 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

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 skoro stejně, ale tentokrát byla kočka namísto názvu souboru předána STDIN stream. Můžete to vidět vyzkoušením s ozvěnou:

$ echo < <(date)
<blank>

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

Potrubí a přesměrování vstupu přesouvají obsah do proudu STDIN. Substituce procesu spustí příkazy, uloží jejich výstup do zvláštního dočasného souboru a potom předá tento název souboru místo příkazu. Ať už používáte jakýkoli příkaz, považuje se to za název souboru. Vytvořený soubor není běžným souborem, ale pojmenovaným kanálem, který se automaticky odstraní, jakmile již nebude potřeba.

140
Caleb

Zde jsou tři věci, které můžete dělat se substitucí procesu, které jsou jinak nemožné.

Více procesních vstupů

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

S trubkami to prostě neexistuje.

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 to spustit přímo. Následující nešťastně selže. Bash již používá ke čtení skriptu STDIN, takže další vstup není možný.

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

Ale tento způsob funguje dokonale.

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

Nahrazení odchozího procesu

Také si všimněte, že substituce procesu funguje také opačně. Takže můžete 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 na /dev/null, zatímco piping stderr do skriptu sed pro extrakci názvů souborů, u nichž se zobrazila chyba „Oprávnění odepřeno“, a poté odešle THOSE 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 pouze výsledek příkazu THAT bude odeslán na /dev/null a se zbytkem řádku to nepořádá.

26
tylerl

Měl bych předpokládat, že mluvíš o bash nebo o nějakém jiném pokročilém Shell, protože posix Shell nemá substituce proces.

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

Proces substituce
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á podobu <(seznam) nebo> (seznam). Seznam procesů je spuštěn se vstupem nebo výstupem připojeným k FIFO nebo nějaký soubor v/dev/fd. Název tohoto souboru je předán jako argument k aktuálnímu příkazu jako výsledek Pokud je použit formulář> (list), zápis do souboru poskytne vstup pro seznam. Pokud se použije formulář <(list), měl by se soubor předaný jako argument přečíst, aby se získal výstup ze seznamu.

Pokud je k dispozici, substituce procesu se provádí současně s expanzí parametrů a proměnných, substitucí příkazů a aritmetickou expanzí.

Jinými slovy az praktického hlediska můžete použít výraz jako je 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 vracím k vaší otázce, zdá se mi, že substituce procesu a potrubí nemají mnoho společného.

Pokud chcete spojit výstup více příkazů, můžete použít jeden z následujících formulářů:

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

ale můžete také použít přesměrování při substituci procesu

command3 < <(command1; command2)

konečně, pokud command3 přijímá parametr souboru (místo stdin)

command3 <(command1; command2)
26
enzotib

Pokud příkaz vezme seznam souborů jako argumenty a zpracuje je jako vstup (nebo výstup, ale ne běžně), každý z těchto souborů může být pojmenovaný kanál nebo pseudo-soubor/dev/fd, který je transparentně poskytnut procesní náhradou:

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

Tím se „rozdělí“ výstup tří příkazů k řazení, protože řazení může mít na příkazovém řádku seznam vstupních souborů.

10
camh

Je třeba poznamenat, že substituce procesu není omezena na formu <(command), která používá výstup command jako soubor. Může to být ve formě >(command), která také posílá soubor jako vstup do command. To je také uvedeno v citaci bash manuálu v odpovědi @ enzotib.

Pro výše uvedený příklad _date | cat_ by byl příkazem, který používá substituci procesu formuláře >(command), k dosažení stejného efektu:

_date > >(cat)
_

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

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

Takže bez _>_ by date >(cat) bylo stejné jako _date /dev/fd/63_, které vytiskne zprávu stderr.

Předpokládejme, že máte program, který bere názvy souborů pouze jako parametry a nezpracovává stdin nebo stdout. Pro ilustraci použiji příliš 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 tomu tak, napište první pole každého řádku _"$1"_ až _"$2"_ pomocí awk. Potom příkaz, který kombinuje vše, co bylo zmíněno, je:

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

To se 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 musíme zde použít náhradu procesu,

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

nebo jeho ekvivalentní podobu

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

Pokud _./psub.sh_ kromě výše uvedeného přečte také stdin, pak takový ekvivalent neexistuje a v tom případě není nic, co bychom mohli použít místo substituce procesu (samozřejmě můžete také použijte pojmenovaný kanál nebo dočasný soubor, ale to je jiný příběh).

3
Weijun Zhou