Hoe kan ik stderr pipen, en niet stdout?

Ik heb een programma dat informatie schrijft naar stdouten stderr, en ik moet de stderrverwerken met grep, stdoutterzijde latend.

Als je een tijdelijk bestand gebruikt, kan dat in twee stappen:

command > /dev/null 2> temp.file
grep 'something' temp.file

Maar hoe kan dit worden bereikt zonder tijdelijke bestanden, met één commando en leidingen?


Antwoord 1, autoriteit 100%

Leid eerst stderr om naar stdout — de pijp; stuur stdout vervolgens door naar /dev/null(zonder te wijzigen waar stderr naartoe gaat):

command 2>&1 >/dev/null | grep 'something'

Zie het hoofdstuk over Omleidingenin de Bash-referentiehandleiding.

Houd er rekening mee dat de volgorde van I/O-omleidingen van links naar rechts wordt geïnterpreteerd, maar dat er leidingen worden ingesteld voordat de I/O-omleidingen worden geïnterpreteerd. Bestandsbeschrijvingen zoals 1 en 2 zijn verwijzingen naar open bestandsbeschrijvingen. De bewerking 2>&1zorgt ervoor dat bestandsdescriptor 2 aka stderr verwijst naar dezelfde open bestandsbeschrijving als bestandsdescriptor 1 aka stdout momenteel verwijst (zie dup2()en open()). De bewerking >/dev/nullverandert dan bestandsdescriptor 1 zodat het verwijst naar een open bestandsbeschrijving voor /dev/null, maar dat verandert niets aan het feit dat bestandsdescriptor 2 verwijst naar de open bestandsbeschrijving waarnaar bestandsdescriptor 1 oorspronkelijk verwees – namelijk de pijp.


Antwoord 2, autoriteit 27%

Of om de uitvoer van standaardfout en standaarduitvoer om te wisselen, gebruik:

command 3>&1 1>&2 2>&3

Dit creëert een nieuwe bestandsdescriptor (3) en wijst deze toe aan dezelfde plaats als 1 (standaarduitvoer), wijst vervolgens fd 1 (standaarduitvoer) toe aan dezelfde plaats als fd 2 (standaardfout) en wijst tenslotte fd 2 toe (standaardfout) naar dezelfde plaats als fd 3 (standaarduitvoer).

Standaardfout is nu beschikbaar als standaarduitvoer en de oude standaarduitvoer blijft behouden in standaardfout. Dit is misschien overdreven, maar het geeft hopelijk meer details over Bash-bestandsdescriptors (er zijn er negen beschikbaar voor elk proces).


Antwoord 3, autoriteit 17%

In Bash kun je ook doorverwijzen naar een subshell met procesvervanging:

command > >(stdlog pipe)  2> >(stderr pipe)

Voor het onderhavige geval:

command 2> >(grep 'something') >/dev/null

Antwoord 4, autoriteit 15%

De beste van deze antwoorden combineren, als u dat doet:

command 2> >(grep -v something 1>&2)

…dan wordt alle stdout bewaard als stdout enalle stderr wordt bewaard als stderr, maar je ziet geen regels in stderr die de tekenreeks “iets” bevatten.

Dit heeft het unieke voordeel dat stdout en stderr niet worden teruggedraaid of weggegooid, of ze samen worden gegooid, en ook geen tijdelijke bestanden worden gebruikt.


Antwoord 5, autoriteit 8%

Het is veel gemakkelijker om dingen te visualiseren als je nadenkt over wat er werkelijk aan de hand is met ‘omleidingen’ en ‘pijpen’. Omleidingen en pijpen in bash doen één ding: wijzigen waar de procesbestanddescriptors 0, 1 en 2 naar verwijzen (zie /proc/[pid]/fd/*).

Als een pijpof “|” operator aanwezig is op de opdrachtregel, is het eerste dat moet gebeuren dat bash een fifo maakt en de FD 1 van het linkercommando naar deze fifo wijst, en de FD 0 van het rechtercommando naar dezelfde fifo.

Vervolgens worden de omleidingsoperatoren voor elke kant geëvalueerd van links naar rechts, en de huidige instellingen worden gebruikt wanneer duplicatie van de descriptor optreedt. Dit is belangrijk omdat de FD1 (linkerkant) en FD0 (rechterkant) sinds de eerste installatie van de pijp al zijn gewijzigd ten opzichte van wat ze normaal gesproken hadden kunnen zijn, en elke verdubbeling hiervan zal dat feit weerspiegelen.

Daarom, wanneer u iets als het volgende typt:

command 2>&1 >/dev/null | grep 'something'

Dit is wat er gebeurt, in volgorde:

  1. er wordt een pijp (fifo) gemaakt. “commando FD1” wordt naar deze pijp gewezen. “grep FD0” wordt ook naar deze pijp verwezen
  2. “commando FD2” wordt verwezen naar waar “commando FD1” momenteel wijst (de pijp)
  3. “commando FD1” verwijst naar /dev/null

Dus alle uitvoer die “commando” naar zijn FD 2 (stderr) schrijft, gaat naar de pijp en wordt gelezen door “grep” aan de andere kant. Alle uitvoer die “commando” naar zijn FD 1 (stdout) schrijft, vindt zijn weg naar /dev/null.

Als u in plaats daarvan het volgende uitvoert:

command >/dev/null 2>&1 | grep 'something'

Dit is wat er gebeurt:

  1. er wordt een pijp gemaakt en “command FD 1” en “grep FD 0” worden ernaar verwezen
  2. “commando FD 1” verwijst naar /dev/null
  3. “commando FD 2” wordt verwezen naar waar FD 1 momenteel naar wijst (/dev/null)

Dus alle stdout en stderr van “command” gaan naar /dev/null. Er gaat niets naar de pijp, en dus wordt “grep” afgesloten zonder iets op het scherm weer te geven.

Houd er rekening mee dat omleidingen (bestandsbeschrijvingen) alleen-lezen (<), alleen-schrijven (>) of lezen-schrijven (<>) kunnen zijn.

Een laatste opmerking. Of een programma iets naar FD1 of FD2 schrijft, is geheel aan de programmeur. Goede programmeerpraktijken schrijven voor dat foutmeldingen naar FD 2 en normale uitvoer naar FD 1 moeten gaan, maar u zult vaak slordige programmering vinden die de twee combineert of anderszins de conventie negeert.


Antwoord 6, autoriteit 3%

Als je Bash gebruikt, gebruik dan:

command >/dev/null |& grep "something"

http://www.gnu.org/software/bash/ manual/bashref.html#Pipelines


Antwoord 7

Voor degenen die stdout en stderr permanent naar bestanden willen omleiden, grep op stderr, maar houd de stdout om berichten naar een tty te schrijven:

# save tty-stdout to fd 3
exec 3>&1
# switch stdout and stderr, grep (-v) stderr for nasty messages and append to files
exec 2> >(grep -v "nasty_msg" >> std.err) >> std.out
# goes to the std.out
echo "my first message" >&1
# goes to the std.err
echo "a error message" >&2
# goes nowhere
echo "this nasty_msg won't appear anywhere" >&2
# goes to the tty
echo "a message on the terminal" >&3

Antwoord 8

Hierdoor wordt command1 stderr omgeleid naar command2 stdin, terwijl command1 stdout ongewijzigd blijft.

exec 3>&1
command1 2>&1 >&3 3>&- | command2 3>&-
exec 3>&-

Genomen van LDP


Antwoord 9

Ik heb zojuist een oplossing bedacht om stdoutnaar het ene commando en stderrnaar het andere te sturen, met behulp van named pipes.

Hier gaat het.

mkfifo stdout-target
mkfifo stderr-target
cat < stdout-target | command-for-stdout &
cat < stderr-target | command-for-stderr &
main-command 1>stdout-target 2>stderr-target

Het is waarschijnlijk een goed idee om de genoemde pijpen daarna te verwijderen.


Antwoord 10

Je kunt de rc shellgebruiken.

Installeer eerst het pakket (het is minder dan 1 MB).

Dit is een voorbeeld van hoe u standaarduitvoer weggooit en standaardfout doorstuurt naar grepin rc:

find /proc/ >[1] /dev/null |[2] grep task

Je kunt het doen zonder Bash te verlaten:

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

Zoals je misschien hebt opgemerkt, kun je specificeren welke bestandsdescriptor je wilt laten doorsluizen door haakjes achter de pijp te gebruiken.

Standaard bestandsdescriptors worden als zodanig genummerd:

  • 0 : Standaard invoer
  • 1 : Standaarduitvoer
  • 2 : Standaardfout

Antwoord 11

Ik probeer te volgen, vind het ook werken,

command > /dev/null 2>&1 | grep 'something'

Other episodes