Bash
Redirection
Zoeken…
Syntaxis
- commando </ pad / naar / bestand # Standaardinvoer omleiden naar bestand
- commando> / pad / naar / bestand # Leid standaarduitvoer naar flie
- commando file_descriptor> / path / to / file # Stuur de output van file_descriptor door naar het bestand
- commando> & file_descriptor # Uitvoer omleiden naar file_descriptor
- opdracht file_descriptor> & another_file_descriptor # Stuur file_descriptor door naar another_file_descriptor
- commando <& file_descriptor # Stuur file_descriptor door naar standaard invoer
- opdracht &> / pad / naar / bestand # Standaarduitvoer en standaardfout naar bestand omleiden
parameters
Parameter | Details |
---|---|
interne bestandsdescriptor | Een geheel getal. |
richting | Een van > , < of <> |
externe bestandsdescriptor of pad | & gevolgd door een geheel getal voor bestandsdescriptor of een pad. |
Opmerkingen
UNIX-consoleprogramma's hebben een invoerbestand en twee uitvoerbestanden (invoer- en uitvoerstromen, evenals apparaten, worden door het besturingssysteem als bestanden behandeld.) Dit zijn meestal het toetsenbord en het scherm, maar alle of alle kunnen worden omgeleid om van - of naar - een bestand of ander programma te komen.
STDIN
is standaardinvoer en is hoe het programma interactieve invoer ontvangt. STDIN
krijgt meestal de bestandsbeschrijving 0 toegewezen.
STDOUT
is standaarduitvoer. Wat wordt uitgezonden op STDOUT
wordt beschouwd als het "resultaat" van het programma. STDOUT
krijgt meestal bestandsbeschrijving 1 toegewezen.
STDERR
is waar foutmeldingen worden weergegeven. Wanneer u een programma uitvoert vanaf de console, wordt STDERR
op het scherm weergegeven en is het niet te onderscheiden van STDOUT
. STDERR
krijgt meestal de bestandsbeschrijving 2 toegewezen.
De volgorde van omleiding is belangrijk
command > file 2>&1
STDERR
zowel ( STDOUT
als STDERR
) naar het bestand STDERR
.
command 2>&1 > file
Hiermee wordt alleen STDOUT
omgeleid, omdat de bestandsdescriptor 2 wordt omgeleid naar het bestand waarnaar wordt verwezen door bestandsdescriptor 1 (wat nog niet het file
is wanneer de instructie wordt geëvalueerd).
Elk commando in een pijplijn heeft zijn eigen STDERR
(en STDOUT
) omdat elk een nieuw proces is. Dit kan verrassende resultaten opleveren als u verwacht dat een omleiding de hele pijplijn zal beïnvloeden. Bijvoorbeeld deze opdracht (verpakt voor leesbaarheid):
$ python -c 'import sys;print >> sys.stderr, "Python error!"' \
| cut -f1 2>> error.log
zal "Python error!" afdrukken naar de console in plaats van het logboekbestand. Voeg in plaats daarvan de fout toe aan de opdracht die u wilt vastleggen:
$ python -c 'import sys;print >> sys.stderr, "Python error!"' 2>> error.log \
| cut -f1
Standaarduitvoer omleiden
>
stuur de standaarduitvoer (ook bekend als STDOUT
) van de huidige opdracht om naar een bestand of een andere descriptor.
Deze voorbeelden schrijven de uitvoer van de opdracht ls
in het bestand file.txt
ls >file.txt
> file.txt ls
Het doelbestand wordt gemaakt als het niet bestaat, anders wordt dit bestand afgekapt.
De standaardomleidingsbeschrijving is de standaarduitvoer of 1
als er geen is opgegeven. Deze opdracht is gelijk aan de vorige voorbeelden, waarbij de standaarduitvoer expliciet is aangegeven:
ls 1>file.txt
Opmerking: de omleiding wordt geïnitialiseerd door de uitgevoerde shell en niet door de uitgevoerde opdracht, daarom wordt dit gedaan vóór de uitvoering van de opdracht.
STDIN omleiden
<
leest uit zijn rechterargument en schrijft naar zijn linkerargument.
Om een bestand te schrijven naar STDIN
we moeten lezen /tmp/a_file
en schrijven in STDIN
dwz 0</tmp/a_file
Opmerking: Interne bestandsdescriptor staat standaard op 0
( STDIN
) voor <
$ echo "b" > /tmp/list.txt
$ echo "a" >> /tmp/list.txt
$ echo "c" >> /tmp/list.txt
$ sort < /tmp/list.txt
a
b
c
Zowel STDOUT als STDERR omleiden
Bestandsdescriptors zoals 0
en 1
zijn pointers. We veranderen waar bestandsomschrijvingen naar verwijzen met omleiding. >/dev/null
betekent 1
punt naar /dev/null
.
Eerst wijzen we 1
( STDOUT
) naar /dev/null
vervolgens punt 2
( STDERR
) naar STDERR
1
STDERR
.
# STDERR is redirect to STDOUT: redirected to /dev/null,
# effectually redirecting both STDERR and STDOUT to /dev/null
echo 'hello' > /dev/null 2>&1
Dit kan verder worden ingekort tot het volgende:
echo 'hello' &> /dev/null
Deze vorm kan echter ongewenst zijn in productie als shell-compatibiliteit een probleem is omdat het conflicteert met POSIX, parsing-dubbelzinnigheid introduceert en shells zonder deze functie het verkeerd interpreteren:
# Actual code
echo 'hello' &> /dev/null
echo 'hello' &> /dev/null 'goodbye'
# Desired behavior
echo 'hello' > /dev/null 2>&1
echo 'hello' 'goodbye' > /dev/null 2>&1
# Actual behavior
echo 'hello' &
echo 'hello' & goodbye > /dev/null
OPMERKING: het is bekend dat &>
naar wens werkt in zowel Bash als Zsh.
STDERR omleiden
2
is STDERR
.
$ echo_to_stderr 2>/dev/null # echos nothing
Definities:
echo_to_stderr
is een opdracht die "stderr"
naar STDERR
echo_to_stderr () {
echo stderr >&2
}
$ echo_to_stderr
stderr
Toevoegen versus Trunceren
Afkappen >
- Maak een gespecificeerd bestand als het niet bestaat.
- Afkappen (inhoud van bestand verwijderen)
- Schrijf naar bestand
$ echo "first line" > /tmp/lines
$ echo "second line" > /tmp/lines
$ cat /tmp/lines
second line
Toevoegen >>
- Maak een gespecificeerd bestand als het niet bestaat.
- Bestand toevoegen (schrijven aan het einde van het bestand).
# Overwrite existing file
$ echo "first line" > /tmp/lines
# Append a second line
$ echo "second line" >> /tmp/lines
$ cat /tmp/lines
first line
second line
STDIN, STDOUT en STDERR uitgelegd
Commando's hebben één ingang (STDIN) en twee soorten uitgangen, standaarduitgang (STDOUT) en standaardfout (STDERR).
Bijvoorbeeld:
STDIN
root@server~# read
Type some text here
Standaardinvoer wordt gebruikt om invoer voor een programma te leveren. (Hier zijn we met behulp van de read
ingebouwde om een regel uit STDIN te lezen.)
STDOUT
root@server~# ls file
file
Standaarduitvoer wordt meestal gebruikt voor "normale" uitvoer van een opdracht. Bijvoorbeeld, ls
geeft bestanden weer, zodat de bestanden naar STDOUT worden verzonden.
STDERR
root@server~# ls anotherfile
ls: cannot access 'anotherfile': No such file or directory
Standaardfout wordt (zoals de naam al aangeeft) gebruikt voor foutmeldingen. Omdat dit bericht geen lijst met bestanden is, wordt het naar STDERR verzonden.
STDIN, STDOUT en STDERR zijn de drie standaard streams. Ze worden aan de schaal geïdentificeerd door een nummer in plaats van een naam:
0 = standaard in
1 = standaard uit
2 = standaardfout
Standaard is STDIN aan het toetsenbord bevestigd en verschijnen zowel STDOUT als STDERR in de terminal. We kunnen STDOUT of STDERR echter omleiden naar wat we nodig hebben. Laten we bijvoorbeeld zeggen dat u alleen de standaarduitgang nodig hebt en dat alle foutmeldingen die op standaardfouten zijn afgedrukt, moeten worden onderdrukt. Dat is wanneer we de descriptoren 1
en 2
.
STDERR omleiden naar / dev / null
In het vorige voorbeeld
root@server~# ls anotherfile 2>/dev/null
root@server~#
In dit geval, als er een STDERR is, wordt deze omgeleid naar / dev / null (een speciaal bestand waarin alles wat erin wordt geplaatst wordt genegeerd), zodat u geen foutuitvoer op de shell krijgt.
Meerdere opdrachten omleiden naar hetzelfde bestand
{
echo "contents of home directory"
ls ~
} > output.txt
Benoemde pijpen gebruiken
Soms wilt u misschien iets door het ene programma uitvoeren en in een ander programma invoeren, maar u kunt geen standaardpijp gebruiken.
ls -l | grep ".log"
U kunt eenvoudig naar een tijdelijk bestand schrijven:
touch tempFile.txt
ls -l > tempFile.txt
grep ".log" < tempFile.txt
Dit werkt prima voor de meeste toepassingen, maar niemand weet wat tempFile
doet en iemand zou het kunnen verwijderen als het de uitvoer van ls -l
in die map bevat. Dit is waar een genoemde pijp in het spel komt:
mkfifo myPipe
ls -l > myPipe
grep ".log" < myPipe
myPipe
is technisch gezien een bestand (alles is in Linux), dus laten we ls -l
in een lege map waarin we zojuist een pipe hebben gemaakt in:
mkdir pipeFolder
cd pipeFolder
mkfifo myPipe
ls -l
De output is:
prw-r--r-- 1 root root 0 Jul 25 11:20 myPipe
Let op het eerste teken in de machtigingen, het wordt weergegeven als een pijp, niet als een bestand.
Laten we nu iets cools doen.
Open een terminal en noteer de map (of maak er een zodat opruimen gemakkelijk is) en maak een pijp.
mkfifo myPipe
Laten we nu iets in de pijp stoppen.
echo "Hello from the other side" > myPipe
Je zult merken dat dit hangt, de andere kant van de pijp is nog steeds gesloten. Laten we de andere kant van de pijp openen en dat spul doorlaten.
Open een andere terminal en ga naar de map waarin de pijp zich bevindt (of als u deze weet, plaats deze dan voor de pijp):
cat < myPipe
Je zult merken dat nadat hello from the other side
is uitgevoerd, het programma in de eerste terminal eindigt, net als dat in de tweede terminal.
Voer de opdrachten nu in omgekeerde volgorde uit. Begin met cat < myPipe
en echo er vervolgens iets in. Het werkt nog steeds, omdat een programma wacht totdat er iets in de pijp wordt gestopt voordat het wordt beëindigd, omdat het weet dat het iets moet krijgen.
Benoemde pijpen kunnen nuttig zijn voor het verplaatsen van informatie tussen terminals of tussen programma's.
Buizen zijn klein. Eenmaal vol, blokkeert de schrijver totdat een lezer de inhoud leest, dus u moet de lezer en de schrijver op verschillende terminals uitvoeren of de ene of de andere op de achtergrond uitvoeren:
ls -l /tmp > myPipe &
cat < myPipe
Meer voorbeelden met benoemde pijpen:
Voorbeeld 1 - alle opdrachten op dezelfde terminal / dezelfde shell
$ { ls -l && cat file3; } >mypipe & $ cat <mypipe # Output: Prints ls -l data and then prints file3 contents on screen
Voorbeeld 2 - alle opdrachten op dezelfde terminal / dezelfde shell
$ ls -l >mypipe & $ cat file3 >mypipe & $ cat <mypipe #Output: This prints on screen the contents of mypipe.
file3
er rekening mee dat de eerste inhoud vanfile3
wordt weergegeven en vervolgens dels -l
gegevens worden weergegeven (LIFO-configuratie).Voorbeeld 3 - alle opdrachten op dezelfde terminal / dezelfde shell
$ { pipedata=$(<mypipe) && echo "$pipedata"; } & $ ls >mypipe # Output: Prints the output of ls directly on screen
$pipedata
dat de variabele$pipedata
niet beschikbaar is voor gebruik in de hoofdterminal / hoofdshell, omdat het gebruik van&
een subshell oproept en$pipedata
alleen beschikbaar was in deze subshell.Voorbeeld 4 - alle opdrachten op dezelfde terminal / dezelfde shell
$ export pipedata $ pipedata=$(<mypipe) & $ ls -l *.sh >mypipe $ echo "$pipedata" #Output : Prints correctly the contents of mypipe
Hierdoor wordt de waarde van
$pipedata
variabele in de hoofdschil correct afgedrukt vanwege de exportaangifte van de variabele. De hoofdterminal / hoofdshell hangt niet vanwege de aanroep van een achtergrondshell (&
).
Foutmeldingen afdrukken naar stderr
Foutmeldingen worden meestal in een script opgenomen voor foutopsporingsdoeleinden of voor een rijke gebruikerservaring. Schrijf eenvoudig een foutmelding zoals deze:
cmd || echo 'cmd failed'
werkt misschien voor eenvoudige gevallen, maar het is niet de gebruikelijke manier. In dit voorbeeld vervuilt het foutbericht de daadwerkelijke uitvoer van het script door zowel fouten als geslaagde uitvoer in stdout
mengen.
Kortom, foutmelding moet naar stderr
gaan en niet naar stdout
. Het is vrij simpel:
cmd || echo 'cmd failed' >/dev/stderr
Een ander voorbeeld:
if cmd; then
echo 'success'
else
echo 'cmd failed' >/dev/stderr
fi
In het bovenstaande voorbeeld wordt het stdout
afgedrukt op stdout
terwijl het foutbericht wordt afgedrukt op stderr
.
Een betere manier om een foutbericht af te drukken, is door een functie te definiëren:
err(){
echo "E: $*" >>/dev/stderr
}
Wanneer u nu een fout moet afdrukken:
err "My error message"
Omleiding naar netwerkadressen
Bash behandelt sommige paden als speciaal en kan wat netwerkcommunicatie doen door te schrijven naar /dev/{udp|tcp}/host/port
. Bash kan geen luisterserver instellen, maar kan een verbinding tot stand brengen en voor TCP kan de resultaten op zijn minst worden gelezen.
Als u bijvoorbeeld een eenvoudig webverzoek wilt verzenden, kunt u het volgende doen:
exec 3</dev/tcp/www.google.com/80
printf 'GET / HTTP/1.0\r\n\r\n' >&3
cat <&3
en de resultaten van de standaardwebpagina van www.google.com
worden afgedrukt naar stdout
.
evenzo
printf 'HI\n' >/dev/udp/192.168.1.1/6666
zou een UDP-bericht met HI\n
naar een luisteraar sturen op 192.168.1.1:6666