Portál AbcLinuxu, 10. května 2025 09:30
mkfifo moje_roura find /usr/share -type d >moje_roura cat moje_roura | grep zoneinfoCo to dela chapu. Ale zaroven jsem si vyzkousel misto roury pouzit obycejny textak kam jsme ladoval data a pak je z neho precetl. Vysledek byl stejny. Muzete mi prosim napsat k cemu se prakticky pojmenovane roury pouzivaji? Pri tom laborovani jsme si vsimnul toho ze kdyz jsem spustil prikaz find tak on nic nedelal az do chvile kdy jsem sputil prikaz cat. Z toho mi vyplyva ze cele to zacne fungovat az ve chvili kdy je na rouru pripojeno neco co zapisuje a zaroven neco co cte. Ale k cemu je tohle zrovna dobre me ted nenapada. Diky za vysvetleni.
Řešení dotazu:
cat fifo | while read url; do wget "$url" doneZaroven pokial uz budes mat prilis vela neprecitanych dat v pipe, tak system zablokuje zapisovatelov. Co moze byt vyhoda alebo aj nie. Ale da sa zapisovat aj tak, aby ma to nezablokovalo ak je plno.
ps ax | grep 'firefox'Tohle je ale anonymní (nepojmenovaná) roura – oba procesy (ten, který do ní zapisuje, i ten, který z ní čte) musíte vytvořit najednou. Pokud ty programy potřebujete spustit odděleně, použijete právě tu pojmenovanou rouru. Typický je případ, kdy v době vytváření prvního procesu (zapisujícího) ještě ani nevíte, co bude tím procesem, který bude data číst – tedy nechcete někde zadrátovat napevno, čím se budou data z roury zpracovávat, ale chcete to ponechat na uživateli. Přesměrujete je tedy do pojmenované roury a uživatel si je odsud už může číst čím chce.
Další možné použití jsou např. multivolume archívy:
$ mkfifo /tmp/tar.fifo $ split -b 100k /tmp/tar.fifo archive.tgz- & tar zcf /tmp/tar.fifo data
cd /tmp touch soubor time cat soubor
cd /tmp mkfifo roura time cat rouraSlovy, u roury nikdy nenactes konec souboru a proto toho muzes vyuzit treba tak, ze cekas na data az se dockas, misto toho aby jsi se musel kazdou chvili kouknout jestli tam uz neco je.
Ale zaroven jsem si vyzkousel misto roury pouzit obycejny textak kam jsme ladoval data a pak je z neho precetl. Vysledek byl stejny.Nebyl. Soubor zůstal na disku. Zabíral místo ale na druhou stranu si takový soubor můžete za rok přečíst znova. Roura (ať už pojmenovaná nebo ne) funguje jinak: co do ní zapíšete, si hned musíte přečíst, a jednou přečtená data už nemůžete přečíst znova. Proto taky zapisovací program čeká na ten čtecí, jinak by zapisoval do prázdna. Na disk se nic neukládá.
Muzete mi prosim napsat k cemu se prakticky pojmenovane roury pouzivaji?Pro cokoliv, kde potřebujete rouru, ale nemůžete použít anonymní rouru
|
. Např. v bashi lze anonymní rouru použít pouze pro přesměrování stdout, ale ne stderr, takže na to druhé musíte mít pojmenovanou rouru. Bash má na tvorbu takové roury pro výstup resp. vstup a napojení dalšího programu nástroje >(program)
a <(program)
.
Není pravda, že: ... v bashi lze anonymní rouru použít pouze pro přesměrování stdout, ale ne stderr ...
$ program 2>&1 |zpracuj_stdout_a_stderr $ program >/dev/null 2>&1 |zpracuj_stderr
Nesouhlas, při startu programu ukazuje file deskriptor stdout na anonymní rouru, ovšem 2>&1
zařídí, že na ní bude ukazovat i file deskriptor stderr, když poté zavolám >/dev/null
bude se na vstupu anonymní roury opravdu odebírat jen stderr, protože stdout je nyní file deskriptor na /dev/null a s anonymní rourou nemá již nic společného.
$ ls -l /proc/PID/fd 0 -> /dev/pts/9 1 -> /dev/null 2 -> pipe:[118037] $ program 2>&1 >/dev/null |zpracuj_stderr
Jinak je velice důležité pořadí viz níže, pokud si nejprve přesměruju stdout do /dev/null, ztratím file deskriptor na anonymní rouru a přesměrováním stderr na stdout přesměruji vlastně na /dev/null.
$ program >/dev/null 2>&1 |nezpracuje_nic
Někomu by se mohlo hodit:
$ program > >(zpracuj_stdout) 2> >(zpracuj_stderr)
Jinak je velice důležité pořadí viz nížeV tom případě to máte v prvním příspěvku špatně.
$ program > >(zpracuj_stdout) 2> >(zpracuj_stderr)
Ano a to jsou ty pojmenované roury jak jsem o nich psal.
Není to tahanice o slovíčka, věci je třeba jednoznačně definovat a důležité je pochopit princip. To není o jiném úhlu pohledu, roura v žádném případě "nebere natvrdo" stdout, ale stdout implicitně ukazuje na zapisovací file descriptor roury, což je dost zásadní rozdíl.
Process Substitution se implementují buď pomocí pojmenovaných rour a nebo pomocí nepojmenovaných. A minimálně na Linuxových systémech používá Bash 3.2 právě roury nepojmenované (anonymní). Viz:
$ vim subst.c static char * process_substitute (string, open_for_read_in_child) char *string; int open_for_read_in_child; { char *pathname; int fd, result; pid_t old_pid, pid; #if defined (HAVE_DEV_FD) int parent_pipe_fd, child_pipe_fd; int fildes[2]; #endif /* HAVE_DEV_FD */ #if defined (JOB_CONTROL) pid_t old_pipeline_pgrp; #endif if (!string || !*string || wordexp_only) return ((char *)NULL); #if !defined (HAVE_DEV_FD) pathname = make_named_pipe (); #else /* HAVE_DEV_FD */ if (pipe (fildes) < 0) { sys_error (_("cannot make pipe for process substitution")); return ((char *)NULL); } /* If OPEN_FOR_READ_IN_CHILD == 1, we want to use the write end of the pipe in the parent, otherwise the read end. */ parent_pipe_fd = fildes[open_for_read_in_child]; child_pipe_fd = fildes[1 - open_for_read_in_child]; /* Move the parent end of the pipe to some high file descriptor, to avoid clashes with FDs used by the script. */ parent_pipe_fd = move_to_high_fd (parent_pipe_fd, 1, 64); pathname = make_dev_fd_filename (parent_pipe_fd); #endif /* HAVE_DEV_FD */
Pokud si opravdu myslíte, že jde o rouru pojmenovanouRoura je pojmenovaná právě tím, že se jmenuje /dev/fd/X.
Čemu z toho co jsem psal nerozumíš? Pokud se použije na Linuxu v Bashi "Process Substitution", tak se vytvoří nepojmenovaná roura pomocí funkce pipe(). Na systémech, kde se nepoužívají nepojmenované roury se používají roury pojmenované a ty se ukládají do $TMPDIR, což není mimochodem v žádném případě /dev/fd! Více informací viz zdrojáky Bashe.
static char * make_named_pipe () { char *tname; tname = sh_mktmpname ("sh-np", MT_USERANDOM); if (mkfifo (tname, 0600) < 0) { free (tname); return ((char *)NULL); } add_fifo_list (tname); return (tname); }
Na závěr Ti ukážu jak vypadá /proc/PID/fd v případě když je stdin jeden konec nepojmenované roury |
nebo > >()
("Process Substitution") a na nakonec jak to vypadá, když se v programu přistupuje k pojmenované rouře vytvořené pomocí mkfifo.
# stdin je předaný pomocí |program lr-x------ 0 -> pipe:[121763] lrwx------ 1 -> /dev/pts/0 lrwx------ 2 -> /dev/pts/0 lr-x------ 4 -> /dev/null # stdin je předaný pomocí > >(program) lr-x------ 0 -> pipe:[123329] lrwx------ 1 -> /dev/pts/0 lrwx------ 2 -> /dev/pts/0 lr-x------ 4 -> /dev/null # program využívá /tmp/named.fifo lrwx------ 0 -> /dev/pts/0 lrwx------ 1 -> /dev/pts/0 lrwx------ 2 -> /dev/pts/0 lr-x------ 3 -> /tmp/named.fifo lr-x------ 4 -> /dev/null $ ls -l /tmp/named.fifo prw-r--r-- /tmp/named.fifo
Tak co, už to chápeš? BTW: Snad nevadí tykání, ja nemám rád, když mi někdo vyká.
program >/dev/null 2>&1 |nezpracuje_nicTento radek posle std out do /dev/null a std err posle na std out, tim padem pujde taky do /dev/null. Napada me jen to ze std err bych musel smerovat primo na terminal ktery ma uzivatel pusteny.
Jak jsem psal výše, pokud chci využít file descriptor stdout, tak ho nejprve musím uložit i do stderr a teprve poté zahodit:
$ program 2>&1 >/dev/null |zpracuj_stderr
File descriptors se dědí od rodičovského procesu.
A co se týče vypisování stderr "na monitor", tak nejprve je třeba si uvědomit, že žádné vypisování "na monitor" neprobíhá. Uvedu několik příkladů.
Naloguju se bez X přímo v terminálu, mám k dispozici Bash, který má k dispozici následující file descriptors. Pokud spustím nějaký program, tak zdědí dceřiný proces tyto descriptory a je pouze na něm jak s nimy naloží.
$ ls -l /dev/fd/ 0 -> /dev/tty2 1 -> /dev/tty2 2 -> /dev/tty2 3 -> /proc/12710/fd
Po spuštění X a Konsole mám zase k dispozici níže uvedené file descriptors.
$ ls -l /dev/fd/ 0 -> /dev/pts/3 1 -> /dev/pts/3 2 -> /dev/pts/3 3 -> /proc/12777/fd 4 -> /dev/null
Takže file descriptors mi vždy implicitně ukazují na terminál kde jsem příkaz spustil tj. o stderr nepřijdu, pokud si ho sám nezničím.
Mrkni se třeba i na další procesy v systému a jejich file descriptors /proc/PID/fd/
.
Proto je např. třeba, když vytváříš daemona nastavit sdtin, stdout, stderr na /dev/null, aby se osamostatnil od rodičovského procesu se vším všudy.
def daemonize(): pid = os.fork() if (pid < 0): sys.exit(1) elif (pid > 0): # exit the parent process sys.exit(0) os.setsid() os.umask(0) pid = os.fork() if (pid < 0): sys.exit(1) elif (pid > 0): # exit the parent process sys.exit(0) stdin = open("/dev/null", "r") stdout = open("/dev/null", "a+") stderr = open("/dev/null", "a+", 0) os.dup2(stdin.fileno(), sys.stdin.fileno()) os.dup2(stdout.fileno(), sys.stdout.fileno()) os.dup2(stderr.fileno(), sys.stderr.fileno())
Už je to trochu jasnější?
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.