Rura służy do przekazywania danych wyjściowych do innego programu lub narzędzia .
Przekierowanie służy do przekazywania danych wyjściowych do pliku lub strumienia .
Przykład: thing1 > thing2
vsthing1 | thing2
thing1 > thing2
- Twoja powłoka uruchomi program o nazwie
thing1
- Wszystko, co
thing1
wyjdzie, zostanie umieszczone w pliku o nazwie thing2
. (Uwaga - jeśli thing2
istnieje, zostanie zastąpiony)
Jeśli chcesz przekazać dane wyjściowe z programu thing1
do programu o nazwie thing2
, możesz wykonać następujące czynności:
thing1 > temp_file && thing2 < temp_file
co by
- uruchom program o nazwie
thing1
- zapisz wynik w pliku o nazwie
temp_file
- uruchom program o nazwie
thing2
, udając, że osoba na klawiaturze wpisała zawartość temp_file
jako dane wejściowe.
Jest to jednak niezgrabne, dlatego stworzyli rury jako prostszy sposób na zrobienie tego. thing1 | thing2
robi to samo cothing1 > temp_file && thing2 < temp_file
EDYCJA, aby podać więcej szczegółów w pytaniu w komentarzu:
Próba >
podania zarówno „przejścia do programu”, jak i „zapisu do pliku” może spowodować problemy w obu kierunkach.
Pierwszy przykład: próbujesz zapisać do pliku. Istnieje już plik o tej nazwie, który chcesz zastąpić. Jednak plik jest wykonywalny. Prawdopodobnie próbowałby uruchomić ten plik, przekazując dane wejściowe. Będziesz musiał zrobić coś takiego, jak zapisać dane wyjściowe pod nową nazwą pliku, a następnie zmienić nazwę pliku.
Drugi przykład: jak zauważył Florian Diesch, co jeśli w innym miejscu systemu istnieje inne polecenie o tej samej nazwie (to znaczy w ścieżce wykonania). Jeśli zamierzasz utworzyć plik o tej nazwie w bieżącym folderze, utkniesz.
Po trzecie: jeśli źle wpiszesz polecenie, nie ostrzeże Cię, że polecenie nie istnieje. W tej chwili, jeśli wpiszesz ls | gerp log.txt
, powie ci bash: gerp: command not found
. Jeśli >
oznaczałoby to jedno i drugie, po prostu utworzyłby dla ciebie nowy plik (a następnie ostrzegł, że nie wie, co z tym zrobić log.txt
).
thing1 > temp_file && thing2 < temp_file
że z rurami możesz zrobić więcej. Ale dlaczego nie użyć ponownie>
operatora, aby to zrobić, np.thing1 > thing2
Dla poleceńthing1
ithing2
? Dlaczego dodatkowy operator|
?less
?thing | less
ithing > less
są zupełnie inni, ponieważ robią różne rzeczy. To, co zaproponujesz, wywołałoby dwuznaczność.tee
polecenie robi coś innego.tee
zapisuje dane wyjściowe zarówno na ekranie (stdout
), jak i pliku. Przekierowanie robi tylko plik.Jeśli znaczenie
foo > bar
zależy od tego, czy istnieje polecenie o nazwie,bar
które spowodowałoby, że użycie przekierowania byłoby znacznie trudniejsze i bardziej podatne na błędy: za każdym razem, gdy chcę przekierować do pliku, najpierw musiałem sprawdzić, czy istnieje polecenie o nazwie jak mój plik docelowy.źródło
bar
katalogu, który jest częścią$PATH
zmiennej env. Jeśli jesteś w / bin /, to może być problem. Ale nawet wtedybar
musiałby mieć ustawione uprawnienia do plików wykonywalnych, aby powłoka nie tylko szukała pliku wykonywalnego,bar
ale faktycznie mogła go wykonać. A jeśli problem dotyczy zastąpienia istniejącego pliku,noclober
opcja powłoki powinna zapobiec zastąpieniu istniejących plików w przekierowaniach.Z Podręcznika administracji systemu Unix i Linux:
Tak więc moja interpretacja brzmi: jeśli jest to polecenie do polecenia, użyj potoku. Jeśli wysyłasz dane do lub z pliku, użyj przekierowania.
źródło
Istnieje zasadnicza różnica między dwoma operatorami:
ls > log.txt
-> To polecenie wysyła dane wyjściowe do pliku log.txt.ls | grep file.txt
-> To polecenie wysyła dane wyjściowe polecenia ls do grep za pomocą potoku (|
), a polecenie grep wyszukuje plik.txt w danych wejściowych dostarczonych mu przez poprzednie polecenie.Jeśli musiałbyś wykonać to samo zadanie przy użyciu pierwszego scenariusza, byłoby to:
Więc potok (with
|
) służy do wysyłania danych wyjściowych do innego polecenia, natomiast przekierowanie (z>
) służy do przekierowania danych wyjściowych do jakiegoś pliku.źródło
Istnieje duża różnica składniowa między nimi:
Można myśleć o przekierowań jak ten:
cat [<infile] [>outfile]
. Oznacza to, że kolejność nie ma znaczenia:cat <infile >outfile
jest taka sama jakcat >outfile <infile
. Możesz nawet łączyć przekierowania z innymi argumentami:cat >outfile <infile -b
icat <infile -b >outfile
oba są całkowicie w porządku. Również można Ciąg razem więcej niż jedno wejście lub wyjście (wejścia będą czytane kolejno, a wszystkie wyjścia zostaną zapisane do każdego pliku wyjściowego)cat >outfile1 >outfile2 <infile1 <infile2
. Celem lub źródłem przekierowania może być nazwa pliku lub nazwa strumienia (jak & 1, przynajmniej w bash).Ale potoki całkowicie oddzielają jedno polecenie od drugiego, nie można ich mieszać z argumentami:
Potok bierze wszystko zapisane na standardowe wyjście z polecenia1 i wysyła je na standardowe wejście polecenia2.
Możesz także łączyć orurowanie i przekierowanie. Na przykład:
Pierwszy
cat
odczyta linie z infile, a następnie jednocześnie zapisze każdą linię do outfile i wyśle ją do drugiejcat
.W drugim
cat
standardowe wejście najpierw czyta z potoku (zawartość infile), a następnie czyta infile2, zapisując każdą linię do outfile2. Po uruchomieniu tego plik wyjściowy będzie kopią pliku infil, a plik outfile2 będzie zawierał plik infile, a następnie plik infile2.Na koniec robisz coś naprawdę podobnego do swojego przykładu, używając przekierowania „tutaj string” (tylko rodzina bash) i backticks:
da taki sam wynik jak
Ale myślę, że wersja przekierowująca najpierw odczyta wszystkie dane wyjściowe ls do bufora (w pamięci), a następnie prześle ten bufor do grepowania po jednej linii na raz, podczas gdy wersja potokowa pobierze każdą linię z ls, gdy się pojawi, i przekaż tę linię grepowi.
źródło
echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blah
Ponadto przekierowanie do pliku będzie korzystało tylko z ostatniego przekierowania.echo yes >/tmp/blah >/tmp/blah2
napisze tylko do/tmp/blah2
.Uwaga: Odpowiedź odzwierciedla moje własne rozumienie tych mechanizmów , zebrane podczas badań i czytania odpowiedzi przez rówieśników na tej stronie i unix.stackexchange.com , i będzie aktualizowana w miarę upływu czasu. Nie wahaj się zadawać pytań lub sugerować ulepszenia w komentarzach. Sugeruję również, aby spróbować zobaczyć, jak syscalls działa w powłoce z
strace
poleceniem. Proszę też nie dać się zastraszyć pojęciem wewnętrznym lub wywołaniami systemowymi - nie musisz ich znać ani być w stanie ich używać, aby zrozumieć, jak działa powłoka, ale zdecydowanie pomagają zrozumieć.TL; DR
|
potoki nie są powiązane z wpisem na dysku, dlatego nie mają i- węzłowego systemu plików na dysku (ale mają i-węzeł w wirtualnym systemie plików pipefs w przestrzeni jądra), ale przekierowania często obejmują pliki, które mają wpisy na dysku, a zatem mają odpowiednie i-węzełlseek()
stanie, więc polecenia nie mogą odczytać niektórych danych, a następnie przewinąć do tyłu, ale gdy przekierowujesz>
lub<
zwykle jest to plik, który jestlseek()
zdolny do obiektu, więc polecenia mogą poruszać się w dowolny sposób.dup2()
wywołania systemowe pod maską, aby zapewnić kopie deskryptorów plików, w których występuje rzeczywisty przepływ danych.exec
wbudowanego polecenia (zobacz to i to ), więc jeśli to zrobisz,exec > output.txt
każde polecenie będzie zapisywaćoutput.txt
od tego momentu.|
potoki są stosowane tylko dla bieżącego polecenia (co oznacza albo polecenie proste, albo polecenie podobne do podpowłokiseq 5 | (head -n1; head -n2)
lub polecenie złożone.Kiedy przekierowanie jest wykonywane na plikach, rzeczy takie jak
echo "TEST" > file
iecho "TEST" >> file
oba używająopen()
syscall na tym pliku ( patrz także ) i pobierają z niego deskryptor pliku, aby go przekazaćdup2()
.|
Używaj tylko rurpipe()
idup2()
syscall.Jeśli chodzi o wykonywane komendy, potoki i przekierowania są niczym więcej niż deskryptorami plików - obiektami podobnymi do plików, do których mogą pisać na ślepo lub manipulować nimi wewnętrznie (co może powodować nieoczekiwane zachowania;
apt
na przykład zwykle nawet nie zapisują na standardowe wyjście jeśli wie, że jest przekierowanie).Wprowadzenie
Aby zrozumieć, czym różnią się te dwa mechanizmy, konieczne jest zrozumienie ich zasadniczych właściwości, historii tych dwóch mechanizmów oraz ich korzeni w języku programowania C. W rzeczywistości niezbędna jest znajomość deskryptorów plików oraz sposobu działania
dup2()
ipipe()
wywołań systemowychlseek()
. Shell ma na celu uczynienie tych mechanizmów abstrakcyjnymi dla użytkownika, ale kopanie głębiej niż abstrakcja pomaga zrozumieć prawdziwą naturę zachowania powłoki.Początki przekierowań i potoków
Zgodnie z artykułem Dennis Ritchie za prorocze Petroglyphs , rury pochodzi z 1964 r wewnętrznej notatce przez Malcolm Douglas McIlroy , w czasie gdy oni pracowali na systemie operacyjnym Multics . Zacytować:
Oczywiste jest, że w tym czasie programy były w stanie zapisywać na dysk, jednak było to nieefektywne, jeśli dane wyjściowe były duże. Cytując wyjaśnienie Briana Kernighana w wideo z Unix Pipeline :
Widoczna jest zatem różnica pojęciowa: potoki są mechanizmem umożliwiającym programom komunikowanie się ze sobą. Przekierowania - są sposobem zapisu do pliku na poziomie podstawowym. W obu przypadkach powłoka ułatwia te dwie rzeczy, ale pod maską dzieje się dużo.
Sięgając głębiej: wywołania systemowe i wewnętrzne działanie powłoki
Zaczynamy od pojęcia deskryptora pliku . Deskryptory plików opisują w zasadzie otwarty plik (czy to plik na dysku, w pamięci, czy plik anonimowy), który jest reprezentowany przez liczbę całkowitą. Dwa standardowe strumienie danych (stdin, stdout, stderr) to odpowiednio deskryptory plików 0,1 i 2. Skąd oni pochodzą ? Cóż, w poleceniach powłoki deskryptory plików są dziedziczone z ich rodzica - powłoki. Dotyczy to ogólnie wszystkich procesów - proces potomny dziedziczy deskryptory plików rodzica. W przypadku demonów powszechne jest zamykanie wszystkich odziedziczonych deskryptorów plików i / lub przekierowywanie do innych miejsc.
Powrót do przekierowania. Co to tak naprawdę jest Jest to mechanizm, który każe powłoce przygotować deskryptory plików dla polecenia (ponieważ przekierowania są wykonywane przez powłokę przed uruchomieniem polecenia) i wskazywać je tam, gdzie sugeruje to użytkownik. Standardowej rozdzielczości z przekierowaniem wyjścia jest
Czy
[n]
istnieje numer deskryptora pliku. Kiedy to zrobisz,echo "Something" > /dev/null
sugeruje się, że liczba 1, iecho 2> /dev/null
.Pod maską odbywa się to poprzez powielenie deskryptora pliku za pomocą
dup2()
wywołania systemowego. Weźmydf > /dev/null
. Powłoka utworzy proces potomny, w którym zostaniedf
uruchomiony, ale wcześniej otworzy się/dev/null
jako deskryptor pliku nr 3 idup2(3,1)
zostanie wydana, co spowoduje utworzenie kopii deskryptora pliku 3, a kopia będzie 1. Wiesz, jak masz dwa plikifile1.txt
ifile2.txt
, a kiedy to zrobiszcp file1.txt file2.txt
, będziesz mieć dwa takie same pliki, ale możesz nimi manipulować niezależnie? To tak samo dzieje się tutaj. Często widać, że przed uruchomieniembash
zrobidup(1,10)
deskryptor pliku kopii nr 1, który jeststdout
(i ta kopia będzie fd # 10) w celu przywrócenia go później. Ważne jest, aby pamiętać, że rozważając wbudowane polecenia(które są częścią samej powłoki i nie mają pliku w/bin
innym miejscu) ani prostych poleceń w nieinteraktywnej powłoce , powłoka nie tworzy procesu potomnego.A potem mamy takie rzeczy jak
[n]>&[m]
i[n]&<[m]
. Jest to powielanie deskryptorów plików, których ten sam mechanizm, codup2()
tylko teraz, znajduje się w składni powłoki, wygodnie dostępnej dla użytkownika.Jedną z ważnych rzeczy na temat przekierowania jest to, że ich kolejność nie jest ustalona, ale ma znaczenie dla tego, jak powłoka interpretuje to, czego chce użytkownik. Porównaj następujące:
Praktyczne wykorzystanie ich w skryptach powłoki może być wszechstronne:
i wiele innych.
Krok po kroku z
pipe()
idup2()
Jak powstają rury? Poprzez
pipe()
syscall , który weźmie jako dane wejściowe tablicę (aka list) wywoływanąpipefd
z dwóch elementów typuint
(liczba całkowita). Te dwie liczby całkowite są deskryptorami plików.pipefd[0]
Będzie czytać koniec rury ipipefd[1]
będzie koniec zapisu. Więc wdf | grep 'foo'
,grep
dostanie kopiępipefd[0]
idf
dostanie kopiępipefd[1]
. Ale jak ? Oczywiście z magiądup2()
syscall. Wdf
naszym przykładzie, powiedzmy, żepipefd[1]
ma numer 4, więc powłoka zrobi dziecko, zróbdup2(4,1)
(pamiętasz mójcp
przykład?), A następnie zrób to,execve()
by uruchomićdf
. Naturalnie,df
odziedziczy deskryptor pliku # 1, ale nie będzie wiedział, że nie wskazuje już terminala, ale w rzeczywistości fd # 4, który jest właściwie końcem zapisu potoku. Oczywiście to samo nastąpi zgrep 'foo'
wyjątkiem różnych liczb deskryptorów plików.Ciekawe pytanie: czy moglibyśmy tworzyć rury, które przekierowują również fd # 2, a nie tylko fd # 1? Tak, w rzeczywistości to właśnie
|&
robi bash. Standard POSIX wymaga języka poleceń powłoki do obsługidf 2>&1 | grep 'foo'
składni w tym celu, ale również tobash
robi|&
.Należy zauważyć, że potoki zawsze zajmują się deskryptorami plików. Istnieje
FIFO
lub nazwany potok , który ma nazwę pliku na dysku i pozwala użyć go jako pliku, ale zachowuje się jak potok. Ale|
typy potoków nazywane są potokami anonimowymi - nie mają nazw plików, ponieważ tak naprawdę są tylko dwoma obiektami połączonymi ze sobą. Fakt, że nie mamy do czynienia z plikami, ma również ważną implikację: potoki nie są wlseek()
stanie. Pliki w pamięci lub na dysku są statyczne - programy mogą używaćlseek()
syscall do przeskakiwania do bajtu 120, następnie z powrotem do bajtu 10, a następnie do przodu do końca. Rury nie są statyczne - są sekwencyjne, dlatego nie można przewijać danych, które z nich otrzymujeszlseek()
. To właśnie powoduje, że niektóre programy są świadome, czy czytają z pliku lub z potoku, i w ten sposób mogą dokonać niezbędnych korekt w celu uzyskania wydajnej wydajności; innymi słowy,prog
mogę wykryć, czy zrobięcat file.txt | prog
lubprog < input.txt
. Przykładem takiej pracy jest ogon .Dwie pozostałe bardzo interesujące właściwości potoków polegają na tym, że mają bufor, który w Linuksie ma 4096 bajtów , i faktycznie mają system plików zdefiniowany w kodzie źródłowym Linuksa ! Nie są po prostu obiektem do przekazywania danych, same są strukturą danych! W rzeczywistości, ponieważ istnieje system plików pipefs, który zarządza zarówno potokami, jak i FIFO, potoki mają numer i- węzła w swoim systemie plików:
W systemie Linux potoki są jednokierunkowe, podobnie jak przekierowanie. W niektórych implementacjach uniksopodobnych - istnieją dwukierunkowe potoki. Chociaż dzięki magii skryptów powłoki można także tworzyć potoki dwukierunkowe w systemie Linux .
Zobacz też:
pipe()
syscall idup2()
.<<
,<<<
są implementowane jako anonimowe (niepowiązane) pliki tymczasowe wbash
iksh
podczas< <()
korzystania z anonimowych potoków;/bin/dash
używa rur do<<
. Zobacz Jaka jest różnica między <<, <<< i <<w bash?źródło
Aby dodać do innych odpowiedzi, istnieją również subtelne różnice semantyczne - np. Potoki zamykają się łatwiej niż przekierowania:
W pierwszym przykładzie, gdy pierwsze wywołanie kończące się,
head
zamyka potok iseq
kończy, więc nie ma danych wejściowych dla drugiegohead
.W drugim przykładzie, head zużywa pierwszą linię, ale kiedy zamyka swój własny
stdin
potok , plik pozostaje otwarty do użycia przy następnym wywołaniu.Trzeci przykład pokazuje, że jeśli użyjemy,
read
aby uniknąć zamknięcia rury, jest ona nadal dostępna w podprocesie.Tak więc „strumień” jest tym, przez co przetaczamy dane (standardowe wyjście itd.), I jest taki sam w obu przypadkach, ale potok łączy strumienie z dwóch procesów, w których przekierowanie łączy strumienie między procesem a plikiem, więc widzę źródło zarówno podobieństw, jak i różnic.
PS Jeśli jesteś tak ciekawy i / lub zaskoczony tymi przykładami jak ja, możesz zagłębić się dalej,
trap
aby zobaczyć, jak procesy się rozwiązują, np .:Czasami pierwszy proces zamyka się przed
1
drukowaniem, a czasem później.Ciekawe jest
exec <&-
również zamknięcie strumienia z przekierowania w celu przybliżenia zachowania potoku (choć z błędem):źródło
read
zużywa tylko pierwszą linię (to jeden bajt dla1
i nowa linia).seq
wysłane łącznie 10 bajtów (5 cyfr i 5 nowych linii). Więc w buforze potoku pozostało 8 bajtów i dlatego drugihead
działa - dane nadal są dostępne w buforze potoku. Btw, głowa wychodzi tylko wtedy, gdy odczytano 0 bajtów, trochę jak whead /dev/null
seq 5 | (head -n1; head -n1)
pierwszym wywołaniu opróżnia potok, więc nadal istnieje w stanie otwartym, ale bez danych dla drugiego wywołaniahead
? Różnica w zachowaniu między potokiem a przekierowaniem polega na tym, że głowa wyciąga wszystkie dane z potoku, ale tylko 2 linie z uchwytu pliku?strace
poleceniu, które podałem w pierwszym komentarzu. Dzięki przekierowaniu plik tmp znajduje się na dysku, dzięki czemu jest widoczny (ponieważ używająlseek()
syscall - polecenia mogą przeskakiwać po pliku od pierwszego bajtu do końca, jak chcą). Ale potoki są sekwencyjne i nie są widoczne. Więc jedyny sposób, aby głowa zrobiła to zadaniem jest przeczytanie wszystkiego na początku, lub jeśli plik jest duży - zamapuj część do pamięci RAM za pomocąmmap()
połączenia. Kiedyś zrobiłem własnytail
w Pythonie i napotkałem dokładnie ten sam problem(...)
, a podpowłoka utworzy kopię swojego standardowego wejścia do każdego polecenia w środku(...)
. Więc są technicznie odczytywane z tego samego obiektu. Najpierwhead
myśli, że czyta ze swojego standardowego wejścia. Po drugiehead
uważa, że ma swój własny standard. Ale w rzeczywistości ich fd # 1 (stdin) jest po prostu kopią tego samego fd, który jest czytany na końcu potoku. Również opublikowałem odpowiedź, więc może pomoże to wyjaśnić.Mam problem z tym dzisiaj w C. Zasadniczo Pipe mają również inną semantykę niż przekierowania, nawet jeśli są wysyłane do
stdin
. Naprawdę myślę, że biorąc pod uwagę różnice, potoki powinny pójść gdzieś indziej niżstdin
, aby pozwolićstdin
i nazwać tostdpipe
(aby zrobić dowolną różnicę) można obsługiwać na różne sposoby.Rozważ to. Podczas przesyłania danych z jednego programu do drugiego
fstat
wydaje się zwracać zero,st_size
mimols -lha /proc/{PID}/fd
że pokazuje, że istnieje plik. Kiedy przekierowanie plik ten nie jest (przynajmniej na Debianiewheezy
,stretch
ajessie
wanilii i ubuntu14.04
,16.04
wanilii.Jeśli masz
cat /proc/{PID}/fd/0
przekierowanie, będziesz mógł powtarzać i czytać tyle razy, ile chcesz. Jeśli zrobisz to za pomocą potoku, zauważysz, że przy drugim uruchomieniu zadania kolejno nie uzyskasz tego samego wyniku.źródło