Peradresuokite stderr ir stdout į bash

Noriu nukreipti ir stdout, ir stderr procesą į vieną failą. Kaip tai padaryti bash?

576
12 марта '09 в 12:14 2009-03-12 12:14 „flybywire “ nustatoma kovo 12 d. 12 val. 12:14 2009-03-12 12:14
@ 13 atsakymų

Pažvelkite čia . Tai turėtų būti:

 yourcommand > 

(nukreipia ir stdout ir stderr į failo pavadinimą).

673
12 марта '09 в 12:17 2009-03-12 12:17 atsakymas pateikiamas dirkgently March 12 '09, 12:17 2009-03-12 12:17
 do_something 2> | tee -a some_file 
border=0

Tai sukels stderr nukreipimą į stdout ir stdout iki some_file ir spausdins standartinę išvestį.

401
12 марта '09 в 12:16 2009-03-12 12:16 Marko atsakymas kovo 12 d. 12 val. 12:16 2009-03-12 12:16

Galite nukreipti stderr į stdout ir stdout į failą:

 some_command >file.log 2>> 

Žr. Http://tldp.org/LDP/abs/html/io-redirection.html.

Šis formatas yra tinkamesnis nei populiariausias formatas, kuris veikia tik bash. „Bourne“ liemenyje jis gali būti interpretuojamas kaip komandos valdymas fone. Taip pat formatas yra skaitomas 2 (STDERR), nukreiptas į 1 (STDOUT).

EDIT: pertvarkomas taip, kaip nurodyta komentaruose.

206
12 марта '09 в 12:17 2009-03-12 12:17 atsakymas pateikiamas f3lix kovo 12 d., 09:17, 2009-03-12 12:17
 # Close STDOUT file descriptor exec 1< # Close STDERR FD exec 2< # Open STDOUT as $LOG_FILE file for read and write. exec 1<>$LOG_FILE # Redirect STDERR to STDOUT exec 2> echo "This line will appear in $LOG_FILE, not 'on screen'" 

Dabar paprastas aidas bus įrašytas į $ LOG_FILE. Naudinga demonizacijai.

Pradinio pranešimo autoriui

Tai priklauso nuo to, ką reikia pasiekti. Jei tiesiog reikia nukreipti komandą iš komandos, kurią skambinate iš savo scenarijaus, atsakymai jau buvo pateikti. Kasyklė nukreipia į dabartinį scenarijų, kuris paveikia visas komandas / įterptas (įskaitant šakutes) po minėto kodo fragmento.


Kitas įdomus sprendimas yra peradresuoti į std-err / out IR į logger arba log failą iš karto, o tai reiškia, kad „srautas“ skirstomas į du. Šią funkciją teikia komanda "tee", kuri gali iškart užrašyti / pridėti keletą failų aprašų (failų, lizdų, kanalų ir tt): Tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...

 exec 3> 4> 1> >(tee >(logger -i -t 'my_script_tag') > 2> >(tee >(logger -i -t 'my_script_tag') > trap 'cleanup' INT QUIT TERM EXIT get_pids_of_ppid() { local ppid="$1" RETVAL='' local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"` RETVAL="$pids" } # Needed to kill processes running in background cleanup() { local current_pid element local pids=( "$$" ) running_pids=("${pids[@]}") while :; do current_pid="${running_pids[0]}" [ -z "$current_pid" ]  break running_pids=("${running_pids[@]:1}") get_pids_of_ppid $current_pid local new_pids="$RETVAL" [ -z "$new_pids" ]  continue for element in $new_pids; do running_pids+=("$element") pids=("$element" "${pids[@]}") done done kill ${pids[@]} 2>/dev/null } 

Taigi, nuo pat pradžių. Tarkime, kad terminalas prijungtas prie / dev / stdout (FD # 1) ir / dev / stderr (FD # 2). Praktiškai tai gali būti vamzdis, lizdas arba kažkas kita.

  • Sukurkite FD # 3 ir # 4 ir nurodykite tą pačią „vietą“, kaip atitinkamai # 1 ir # 2. FD # 1 keitimas nuo šiol neturi įtakos FD # 3. Dabar FD # 3 ir # 4 nurodo atitinkamai STDOUT ir STDERR. Jie bus naudojami kaip tikrieji STDOUT ir STDERR terminalai.
  • 1 → (...) peradresuoja stdout komandai parenose
  • Parens (sub-shell) skaito "tee" iš exec STDOUT (vamzdis) ir peradresuoja komandą "logger" per kitą kanalą į sub-shell į parenus. Tuo pačiu metu jis kopijuoja tą patį įrašą FD # 3 (terminalas)
  • antroji dalis, labai panaši, susijusi su tuo pačiu triku STDERR ir FD # 2 ir # 4.

Scenarijos rezultatas, turintis aukščiau nurodytą eilutę, ir papildomai:

 echo "Will end up in STDOUT(terminal) and /var/log/messages" 

... atrodo taip:

 $ ./my_script Will end up in STDOUT(terminal) and /var/log/messages $ tail -n1 /var/log/messages Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages 

Jei norite matyti aiškesnį vaizdą, pridėkite šias dvi eilutes prie scenarijaus:

 ls -l /proc/self/fd/ ps xf 
173
13 дек. atsakymas pateikiamas 13 d. 2013-12-13 13:30 '13, 13:30, 2013-12-13 13:30
 bash your_script.sh 1>file.log 2>> 

1>file.log sako, kad korpusas siunčia STDOUT į failą file.log , o 2>> nurodo peradresuoti STDERR (failo deskriptorius 2) į STDOUT (failo deskriptorius 1).

Pastaba: Užsisakykite klausimus, kaip nurodyta liw.fi, 2> 1>file.log neveikia.

35
12 марта '09 в 12:17 2009-03-12 12:17 atsakymas, kurį pateikė Guðmundur H kovo 12 d., 09:17, 2009-03-12 12:17

Smalsu, kad ji veikia:

 yourcommand  filename 

Tačiau tai suteikia sintaksės klaidą:

 yourcommand  filename syntax error near unexpected token `>' 

Turėtumėte naudoti:

 yourcommand 1>> filename 2>> 
20
06 мая '09 в 17:12 2009-05-06 17:12 atsakymas, kurį pateikė Mark 06 gegužės 09 d. 17:12 2009-05-06 17:12

Trumpas atsakymas yra: Command >filename 2>> arba Command >


Paaiškinimas:

Apsvarstykite šį kodą, kuris stdout įrašo žodį „stdout“ ir žodį „stderror“ ant stderror.

 $ (echo "stdout"; echo "stderror" > stdout stderror 

Atkreipkite dėmesį, kad „ operatorius pasakoja bash, kad 2 yra failų deskriptorius (kuris nurodo stderr), o ne failo pavadinimas. Jei stderror " ši komanda bus spausdinti stdout stdout ir sukurti failą pavadinimu "2" ir rašyti stderror .

Eksperimentuodami aukščiau pateiktą kodą, galite pamatyti, kaip veikia peradresavimo operatoriai. Pvz., Keičiant failą, kuris iš dviejų 1,2 ių deskriptorių nukreipiamas į /dev/null kitos dvi kodo eilutės pašalins viską nuo stdout ir viskas nuo stderror (spausdinimas lieka).

 $ (echo "stdout"; echo "stderror" > 1>/dev/null stderror $ (echo "stdout"; echo "stderror" > 2>/dev/null stdout 

Dabar mes galime paaiškinti, kodėl sprendimas, kodėl šis kodas nesukuria produkcijos:

 (echo "stdout"; echo "stderror" > >/dev/null 2>> 

Norint tai suprasti, labai rekomenduoju perskaityti šį tinklalapį failų aprašų lentelėse . Darant prielaidą, kad atlikote šį skaitymą, galime tęsti. Atkreipkite dėmesį, kad Bash procesai iš kairės į dešinę; todėl Bash pirmą kartą mato >/dev/null (kuris yra 1>/dev/null ) ir nustato failo deskriptorių 1, nurodantį / dev / null, o ne stdout. Tai padarius, Bash perkelia į dešinę ir mato 2>> . Tai nustato failo deskriptorių 2 , nurodantį tą patį failą kaip 1 failo deskriptorius (o ne failo deskriptorius 1 pats !!!! (žr. Šį šaltinį, skirtą daugiau informacijos). Kadangi failo deskriptorius 1 nurodo į / dev / null, o failo deskriptorius 2 nurodo tą patį failą kaip 1 failo deskriptorius, failo deskriptorius 2 taip pat nurodo / dev / null. Taigi abu failų deskriptoriai nurodo / dev / null, todėl rezultatas nėra išvedamas.


Jei norite patikrinti, ar tikrai suprantate šią koncepciją, bandykite atspėti rezultatą, kai perjungiame peradresavimo tvarką:

 (echo "stdout"; echo "stderror" > 2> >/dev/null 

stderror

Pavyzdys yra tai, kad balas iš kairės į dešinę, Bash mato 2> 1 ir tokiu būdu nustato failo deskriptorių 2 į tą pačią vietą kaip 1 failo deskriptorius, ty stdout. Tada jis nustato failo deskriptorių 1 (nepamirškite, kad> / dev / null = 1> / dev / null) nukreipti į> / dev / null, taip pašalinant viską, kas paprastai būtų siunčiama į standartinę išvestį. Taigi viskas, kas mums liko, yra tai, kuri nebuvo išsiųsta į stdout subshell (kodą skliausteliuose), tai yra, "stderror". Įdomu pažymėti, kad nors 1 yra tik rodyklė į stdout, nukreipiant rodyklę nuo 2 iki 1 iki 2>> , NE sudaro formų 2 → 1 → stdout grandinę. Jei taip atsitiko, nukreipus 1 į / dev / null, kodas 2> >/dev/null suteiktų 2 → 1 -> / dev / null žymių grandinę, todėl kodas nesukurs nieko, skirtingai nei apie tai, ką matėme anksčiau.


Galiausiai norėčiau pabrėžti, kad yra lengviau tai padaryti:

Iš 3.6.4 skirsnio aišku, kad galime naudoti > operatorių nukreipti ir stdout, ir stderr. Taigi, norėdami nukreipti stderr ir stdout išvestį į bet kokią komandą, kuri yra \dev\null (kuri pašalina išvestį), mes tiesiog įvedame $ command > arba mano pavyzdžio atveju:

 $ (echo "stdout"; echo "stderror" > > 

Raktiniai raktai:

  • Failų deskriptoriai elgiasi kaip rodikliai (nors failų aprašai nesutampa su failų rodyklėmis)
  • Peradresuojant failo deskriptorių "a" į failo deskriptorių "b", kuris nurodo failą "f", failo deskriptorius "a" nukreipia į tą pačią vietą kaip failo deskriptorius b - failas "f". Jis nesudaro nuorodų a → b → f grandinės
  • Dėl pirmiau išdėstytų priežasčių užsakymo klausimai, 2> >/dev/null yra! = >/dev/null 2>> . Vienas generuoja produkciją, o kitas ne!

Galiausiai pažiūrėkite į šiuos puikius išteklius:

„Bash“ dokumentacija, skirta peradresavimui , failų deskriptorių lentelių paaiškinimas , įvadai į rodykles

10
19 нояб. Atsakymą pateikė Evan Rosica lapkričio 19 d. 2017-11-19 04:55 '17 at 4:55 2017-11-19 04:55
 LOG_FACILITY="local7.notice" LOG_TOPIC="my-prog-name" LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]" LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]" exec 3> > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" ) exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" ) 

Tai susiję su: stdOut ir stderr rašykite syslog.

Jis beveik veikia, bet ne iš xinted; (

8
23 апр. atsakymas pateikiamas log-control 23 d 2009-04-23 16:14 '09, 16:14, 2009-04-23 16:14

Norėjau, kad sprendimas išvestų iš stdout plus stderr išvestį į žurnalo failą, o stderr vis dar yra konsolėje. Taigi man reikėjo dubliuoti stderr išvestį per tee.

Tai yra sprendimas, kurį radau:

 command 3> 1> 2> 1>>logfile | tee -a logfile 
  • Pirmiausia pakeiskite stderr ir stdout
  • tada pridėkite stdout į žurnalo failą
  • vamzdis stderr į tee ir pridėti jį taip pat į žurnalo failą
5
19 янв. atsakymas pateikiamas bebbo 19 sausio. 2017-01-19 22:21 '17, 10:21 pm 2017-01-19 22:21

Paprasčiausias būdas (tik bash4): ls * 2> 1>> .

1
04 февр. atsakymas pateikiamas 04 vasario mėn. 2016-02-04 16:57 '16 at 16:57 2016-02-04 16:57

Toliau išvardytos funkcijos gali būti naudojamos automatizuojant beetwen stdout / stderr ir log failo išėjimų procesą.

 #!/bin/bash #set -x # global vars OUTPUTS_REDIRECTED="false" LOGFILE=/dev/stdout # "private" function used by redirect_outputs_to_logfile() function save_standard_outputs { if [ "$OUTPUTS_REDIRECTED" == "true" ]; then echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before" exit 1; fi exec 3> exec 4> trap restore_standard_outputs EXIT } # Params: $1 => logfile to write to function redirect_outputs_to_logfile { if [ "$OUTPUTS_REDIRECTED" == "true" ]; then echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before" exit 1; fi LOGFILE=$1 if [ -z "$LOGFILE" ]; then echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]" fi if [ ! -f $LOGFILE ]; then touch $LOGFILE fi if [ ! -f $LOGFILE ]; then echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]" exit 1 fi save_standard_outputs exec 1>>${LOGFILE%.log}.log exec 2> OUTPUTS_REDIRECTED="true" } # "private" function used by save_standard_outputs() function restore_standard_outputs { if [ "$OUTPUTS_REDIRECTED" == "false" ]; then echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected" exit 1; fi exec 1> #closes FD 1 (logfile) exec 2> #closes FD 2 (logfile) exec 2> #restore stderr exec 1> #restore stdout OUTPUTS_REDIRECTED="false" } 

Pavyzdys, kaip naudoti scenarijų:

 echo "this goes to stdout" redirect_outputs_to_logfile /tmp/one.log echo "this goes to logfile" restore_standard_outputs echo "this goes to stdout" 
1
26 июля '18 в 18:53 2018-07-26 18:53 atsakymą pateikė Fernando Fabreti liepos 26 d., 18 val. 18:53 val. 18:53

Tcsh atveju turiu naudoti šią komandą:

 command > file 

Jei naudojama command file , ji sukurs „Invalid Empty Command“ klaidą.

0
23 апр. atsakymas pateikiamas einstein6 23 d. 2013-04-23 08:07 '13, 08:07 2013-04-23 08:07

@ Fernando-fabreti

Pridėjus tai, ką darėte, šiek tiek pakeitiau funkcijas ir ištrinau „- close“, ir jis dirbo man.

  function saveStandardOutputs { if [ "$OUTPUTS_REDIRECTED" == "false" ]; then exec 3> exec 4> trap restoreStandardOutputs EXIT else echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before" exit 1; fi } # Params: $1 => logfile to write to function redirectOutputsToLogfile { if [ "$OUTPUTS_REDIRECTED" == "false" ]; then LOGFILE=$1 if [ -z "$LOGFILE" ]; then echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]" fi if [ ! -f $LOGFILE ]; then touch $LOGFILE fi if [ ! -f $LOGFILE ]; then echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]" exit 1 fi saveStandardOutputs exec 1>>${LOGFILE} exec 2> OUTPUTS_REDIRECTED="true" else echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before" exit 1; fi } function restoreStandardOutputs { if [ "$OUTPUTS_REDIRECTED" == "true" ]; then exec 1> #restore stdout exec 2> #restore stderr OUTPUTS_REDIRECTED="false" fi } LOGFILE_NAME="tmp/one.log" OUTPUTS_REDIRECTED="false" echo "this goes to stdout" redirectOutputsToLogfile $LOGFILE_NAME echo "this goes to logfile" echo "${LOGFILE_NAME}" restoreStandardOutputs echo "After restore this goes to stdout" 
0
14 сент. Atsakymas yra suteiktas toms schumacher 14 sep . 2018-09-14 19:35 '18, 07:35 pm 2018-09-14 19:35

Kiti klausimai apie „ žymių arba „ Klauskite“