Captura stdout y stderr en diferentes variables
¿Es posible almacenar o capturar stdout y stderr en diferentes variables, sin usar un archivo temporal? Ahora mismo hago esto para obtener stdout en out
y stderr en err
al ejecutar some_command
, pero
como para evitar el archivo temporal.
error_file=$(mktemp)
out=$(some_command 2>$error_file)
err=$(< error_file)
rm $error_file
13 answers
Ok, se puso un poco feo, pero aquí hay una solución:
unset t_std t_err
eval "$( (echo std; echo err >&2) \
2> >(readarray -t t_err; typeset -p t_err) \
> >(readarray -t t_std; typeset -p t_std) )"
Donde (echo std; echo err >&2)
necesita ser reemplazado por el comando real. La salida de stdout se guarda en el array $t_std
línea por línea omitiendo las nuevas líneas (el -t
) y stderr en $t_err
.
Si no te gustan los arrays puedes hacer{[13]]}
unset t_std t_err
eval "$( (echo std; echo err >&2 ) \
2> >(t_err=$(cat); typeset -p t_err) \
> >(t_std=$(cat); typeset -p t_std) )"
Que prácticamente imita el comportamiento de var=$(cmd)
excepto por el valor de $?
que nos lleva a la última modificación:
unset t_std t_err t_ret
eval "$( (echo std; echo err >&2; exit 2 ) \
2> >(t_err=$(cat); typeset -p t_err) \
> >(t_std=$(cat); typeset -p t_std); t_ret=$?; typeset -p t_ret )"
Aquí $?
es preserved into $t_ret
Probado en Debian wheezy usando GNU bash
, Versión 4.2.37(1)-release (i486-pc-linux-gnu).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-22 14:22:39
Jonathan tiene la respuesta. Como referencia, este es el truco de ksh93. (requiere una versión no antigua).
function out {
echo stdout
echo stderr >&2
}
x=${ { y=$(out); } 2>&1; }
typeset -p x y # Show the values
Produce
x=stderr
y=stdout
La sintaxis ${ cmds;}
es solo una sustitución de comandos que no crea una subcapa. Los comandos se ejecutan en el entorno de shell actual. El espacio al principio es importante ({
es una palabra reservada).
Stderr del grupo de comandos interno se redirige a stdout (de modo que se aplica a la sustitución interna). Siguiente, el stdout de out
se asigna a y
, y el stderr redirigido es capturado por x
, sin la pérdida habitual de y
a una subcapa de sustitución de comandos.
No es posible en otros shells, porque todas las construcciones que capturan la salida requieren poner al productor en una subcapa, que en este caso, incluiría la asignación.
actualización: Ahora también soportado por mksh.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:32
Este comando establece los valores stdout (stdval) y stderr (errval) en el shell actual:
eval "$( execcommand 2> >(setval errval) > >(setval stdval); )"
Siempre que se haya definido esta función:
function setval { printf -v "$1" "%s" "$(cat)"; declare -p "$1"; }
Cambie execcommand al comando capturado, ya sea "ls", "cp", "df", etc.
Todo esto se basa en la idea de que podríamos convertir todos los valores capturados en una línea de texto con la ayuda de la función setval, luego setval se utiliza para capturar cada valor en esta estructura:
execcommand 2> CaptureErr > CaptureOut
Convertir cada uno captura de valor a una llamada setval:
execcommand 2> >(setval errval) > >(setval stdval)
Envuelva todo dentro de una llamada execute y haga eco de ella:
echo "$( execcommand 2> >(setval errval) > >(setval stdval) )"
Obtendrá las llamadas de declaración que crea cada setval:
declare -- stdval="I'm std"
declare -- errval="I'm err"
Para ejecutar ese código (y obtener el conjunto vars) use eval:
eval "$( execcommand 2> >(setval errval) > >(setval stdval) )"
Y finalmente hacer eco de los vars conjunto:
echo "std out is : |$stdval| std err is : |$errval|
También es posible incluir el valor return (exit).
Un ejemplo completo de script bash se ve así:
#!/bin/bash --
# The only function to declare:
function setval { printf -v "$1" "%s" "$(cat)"; declare -p "$1"; }
# a dummy function with some example values:
function dummy { echo "I'm std"; echo "I'm err" >&2; return 34; }
# Running a command to capture all values
# change execcommand to dummy or any other command to test.
eval "$( dummy 2> >(setval errval) > >(setval stdval); <<<"$?" setval retval; )"
echo "std out is : |$stdval| std err is : |$errval| return val is : |$retval|"
To sum everything up for the benefit of the reader, here is an
Solución fácil de usar bash
Esta versión utiliza subconchas y se ejecuta sin tempfile
s. (Para una versión tempfile
que se ejecuta sin subconchas, ver mi otra respuesta.)
: catch STDOUT STDERR cmd args..
catch()
{
eval "$({
__2="$(
{ __1="$("${@:3}")"; } 2>&1;
ret=$?;
printf '%q=%q\n' "$1" "$__1" >&2;
exit $ret
)"
ret="$?";
printf '%s=%q\n' "$2" "$__2" >&2;
printf '( exit %q )' "$ret" >&2;
} 2>&1 )";
}
Ejemplo de uso:
dummy()
{
echo "$3" >&2
echo "$2" >&1
return "$1"
}
catch stdout stderr dummy 3 $'\ndiffcult\n data \n\n\n' $'\nother\n difficult \n data \n\n'
printf 'ret=%q\n' "$?"
printf 'stdout=%q\n' "$stdout"
printf 'stderr=%q\n' "$stderr"
Esto imprime
ret=3
stdout=$'\ndiffcult\n data '
stderr=$'\nother\n difficult \n data '
Así que se puede usar sin pensar más en ello. Simplemente pon catch VAR1 VAR2
delante de cualquier command args..
y estás Terminado.
Algunos if cmd args..; then
se convertirán en if catch VAR1 VAR2 cmd args..; then
. Realmente nada complejo.
Discusión
P: ¿Cómo funciona?
Simplemente envuelve las ideas de las otras respuestas aquí en una función, de tal manera que puede ser fácilmente reutilizada.
catch()
básicamente usa eval
para establecer las dos variables. Esto es similar a https://stackoverflow.com/a/18086548
Considere una llamada de catch out err dummy 1 2a 3b
:
Saltemos el
eval "$({
y el__2="$(
, por ahora. Hablaré de esto más tarde.__1="$("$("${@:3}")"; } 2>&1;
ejecutadummy 1 2 3
y tiendas esstdout
en__1
para su uso posterior. Así que__1
se convierte en2a
. También redirigestderr
dedummy
astdout
, de modo que la captura exterior puede reunirsestdout
ret=$?;
coge el código de salida, que es1
printf '%q=%q\n' "$1" "$__1" >&2;
luego saleout=2a
astderr
.stderr
aquí se utiliza, como la actualstdout
ya ha asumido el papel destderr
de ladummy
comando.exit $ret
luego reenvía el código de salida (1
) a la siguiente etapa.
Ahora hacia el exterior __2="$( ... )"
:
Esto captura
stdout
de lo anterior, que es elstderr
de la llamadadummy
, en la variable__2
. (Podríamos reutilizar__1
aquí, pero usé__2
para hacerlo menos confuso.). Así que__2
se convierte3b
-
ret="$?";
captura el código de retorno (devuelto)1
(dedummy
) de nuevo printf '%s=%q\n' "$2" "$__2" >&2;
luego saleerr=3a
astderr
.stderr
se usa de nuevo, ya que ya se usó para generar la otra variableout=2a
.printf '( exit %q )' "$ret" >&2; then outputs the code to set the proper return value. I did not find a better way, as assignig it to a variable needs a variable name, which then cannot be used as first oder second argument to
catch`.
Tenga en cuenta que, como una optimización, podríamos haber escrito esos 2 printf
como uno solo como printf '%s=%q\n( exit %q )
"$__2" "$ret "' también.
Entonces, ¿qué tenemos hasta ahora?
Tenemos lo siguiente escrito a stderr: {[109]]}
out=2a
err=3b
( exit 1 )
De donde out
es de $1
, 2a
es de stdout
de dummy
, err
es de $2
, 3b
es de stderr
de dummy
, y 1
es desde el código de retorno de dummy
.
Tenga en cuenta que %q
en el formato de printf
tiene cuidado de citar, de modo que el shell ve argumentos adecuados (individuales) cuando se trata de eval
. 2a
y 3b
son tan simples, que se copian literalmente.
Ahora hacia el exterior eval "$({ ... } 2>&1 )";
:
Esto ejecuta todo lo anterior que genera las 2 variables y el exit
, lo captura (por lo tanto el 2>&1
) y lo analiza en el shell actual usando eval
.
De esta manera se establecen las 2 variables y también el código de retorno.
P: Utiliza eval
que es malo. Entonces, ¿es seguro?
- Mientras
printf %q
no tenga errores, debería ser seguro. Pero siempre tienes que tener mucho cuidado, solo piensa en ShellShock.
P: ¿Errores?
-
No se conocen errores obvios, excepto siguiente:
- La captura de una salida grande necesita una gran memoria y CPU, ya que todo va en variables y necesita ser analizado de nuevo por el shell. Así que úsalo sabiamente.
-
Como de costumbre
$(echo $'\n\n\n\n')
traga todos los alimentos de línea, no solo el último. Este es un requisito de POSIX. Si necesita obtener el LFs ileso, simplemente agregue un carácter final a la salida y elimínelo después como en la siguiente receta (mire el finalx
que permite leer un softlink apuntando a un archivo que termina en un$'\n'
):target="$(readlink -e "$file")x" target="${target%x}"
Las variables Shell no pueden llevar el byte NUL (
$'\0'
). Simplemente se ignoran si ocurren enstdout
ostderr
.
El comando dado se ejecuta en un sub-subshell. Por lo tanto, no tiene acceso a
$PPID
, ni puede alterar las variables del shell. Puedecatch
una función de shell, incluso builtins, pero esos no serán capaces de alterar las variables de shell (como todo lo que se ejecuta dentro$( .. )
no puede hacer esto). Así que si necesita ejecutar una función en el shell actual y capturar su stderr / stdout, necesita hacer esto de la manera habitual contempfile
s. (Hay maneras de hacer esto tales, que interrumpir el shell normalmente no deja residuos atrás, pero esto es complejo y merece su propia respuesta.)
P: ¿Versión Bash?
- Creo que necesitas Bash 4 y superior (debido a
printf %q
)
P: Esto todavía se ve así torpe.
- Correcto. Otra respuesta aquí muestra cómo se puede hacer en
ksh
mucho más limpiamente. Sin embargo, no estoy acostumbrado aksh
, así que dejo que otros creen una receta similar fácil de reutilizar paraksh
.
P: ¿Por qué no usar ksh
entonces?
- Porque esta es una solución
bash
P: El script se puede mejorar
- Por supuesto, puede exprimir algunos bytes y crear más pequeños o más solución incomprensible. Solo tienes que ir a por ello;)
P: Hay un error tipográfico. : catch STDOUT STDERR cmd args..
debe decir # catch STDOUT STDERR cmd args..
- En realidad esto es lo que se pretende.
:
aparece enbash -x
mientras los comentarios se tragan silenciosamente. Así que puedes ver dónde está el analizador si tienes un error tipográfico en la definición de la función. Es un viejo truco de depuración. Pero ten cuidado un poco, puedes crear fácilmente algunos efectos secundarios dentro de los argumentos de:
.
Editar: Se agregaron un par más ;
para que sea más fácil crear un solo revestimiento a partir de catch()
. Y se agregó la sección cómo funciona.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:38
Técnicamente, los pipes con nombre no son archivos temporales y nadie los menciona aquí. No almacenan nada en el sistema de archivos y puedes eliminarlos tan pronto como los conectes (para que nunca los veas):
#!/bin/bash -e
foo () {
echo stdout1
echo stderr1 >&2
sleep 1
echo stdout2
echo stderr2 >&2
}
rm -f stdout stderr
mkfifo stdout stderr
foo >stdout 2>stderr & # blocks until reader is connected
exec {fdout}<stdout {fderr}<stderr # unblocks `foo &`
rm stdout stderr # filesystem objects are no longer needed
stdout=$(cat <&$fdout)
stderr=$(cat <&$fderr)
echo $stdout
echo $stderr
exec {fdout}<&- {fderr}<&- # free file descriptors, optional
Puede tener múltiples procesos en segundo plano de esta manera y recopilar asincrónicamente sus stdouts y stderrs en un momento conveniente, etc.
Si necesita esto para un solo proceso, también puede usar números fd codificados como 3 y 4, en lugar de la sintaxis {fdout}/{fderr}
(que encuentra un fd gratis para usted).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-16 09:06:33
A
No le gustó la evaluación, así que aquí hay una solución que usa algunos trucos de redirección para capturar la salida del programa a una variable y luego analiza esa variable para extraer los diferentes componentes. El indicador-w establece el tamaño del fragmento e influye en el orden de los mensajes std-out/err en el formato intermedio. 1 da potencialmente alta resolución a costa de gastos generales.
#######
# runs "$@" and outputs both stdout and stderr on stdin, both in a prefixed format allowing both std in and out to be separately stored in variables later.
# limitations: Bash does not allow null to be returned from subshells, limiting the usefullness of applying this function to commands with null in the output.
# example:
# var=$(keepBoth ls . notHere)
# echo ls had the exit code "$(extractOne r "$var")"
# echo ls had the stdErr of "$(extractOne e "$var")"
# echo ls had the stdOut of "$(extractOne o "$var")"
keepBoth() {
(
prefix(){
( set -o pipefail
base64 -w 1 - | (
while read c
do echo -E "$1" "$c"
done
)
)
}
( (
"$@" | prefix o >&3
echo ${PIPESTATUS[0]} | prefix r >&3
) 2>&1 | prefix e >&1
) 3>&1
)
}
extractOne() { # extract
echo "$2" | grep "^$1" | cut --delimiter=' ' --fields=2 | base64 --decode -
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-08 10:41:34
Sucintamente, creo que la respuesta es 'No'. La captura $( ... )
solo captura la salida estándar de la variable; no hay una manera de obtener el error estándar capturado en una variable separada. Por lo tanto, lo que tienes es casi tan limpio como se pone.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-14 06:33:42
Qué pasa... = D
GET_STDERR=""
GET_STDOUT=""
get_stderr_stdout() {
GET_STDERR=""
GET_STDOUT=""
unset t_std t_err
eval "$( (eval $1) 2> >(t_err=$(cat); typeset -p t_err) > >(t_std=$(cat); typeset -p t_std) )"
GET_STDERR=$t_err
GET_STDOUT=$t_std
}
get_stderr_stdout "command"
echo "$GET_STDERR"
echo "$GET_STDOUT"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-10 17:04:52
Para el beneficio del lector aquí hay una solución usando tempfile
s.
La cuestión era no usar tempfile
s. Sin embargo, esto podría deberse a la contaminación no deseada de /tmp/
con tempfile en caso de que el shell muera. En el caso de kill -9
algunos trap 'rm "$tmpfile1" "$tmpfile2"' 0
no se disparan.
Si se encuentra en una situación en la que puede utilizar tempfile
, pero quiero nunca dejar escombros detrás , aquí hay una receta.
De nuevo se llama catch()
(como mi otra respuesta) y tiene la misma sintaxis de llamada:
catch stdout stderr command args..
# Wrappers to avoid polluting the current shell's environment with variables
: catch_read returncode FD variable
catch_read()
{
eval "$3=\"\`cat <&$2\`\"";
# You can use read instead to skip some fork()s.
# However read stops at the first NUL byte,
# also does no \n removal and needs bash 3 or above:
#IFS='' read -ru$2 -d '' "$3";
return $1;
}
: catch_1 tempfile variable comand args..
catch_1()
{
{
rm -f "$1";
"${@:3}" 66<&-;
catch_read $? 66 "$2";
} 2>&1 >"$1" 66<"$1";
}
: catch stdout stderr command args..
catch()
{
catch_1 "`tempfile`" "${2:-stderr}" catch_1 "`tempfile`" "${1:-stdout}" "${@:3}";
}
Lo que hace:
Crea dos
tempfile
s parastdout
ystderr
. Sin embargo, casi inmediatamente elimina estos, de tal manera que solo están alrededor por un tiempo muy corto.catch_1()
capturastdout
(FD 1) en una variable y se muevestderr
astdout
, de modo que el siguiente ("izquierdo")catch_1
puede capturar eso.El procesamiento en
catch
se realiza de derecha a izquierda, por lo que elcatch_1
izquierdo se ejecuta el último y capturasstderr
.
Lo peor que puede suceder es que algunos archivos temporales aparezcan en /tmp/
, pero siempre están vacíos en ese caso. (Se quitan antes de que se llenen.). Por lo general, esto no debería ser un problema, ya que bajo Linux tmpfs soporta aproximadamente 128K archivos por GB de memoria principal.
El comando dado también puede acceder y alterar todas las variables locales del shell. Así que usted puede llamar a una función de shell que tiene sideffects!
Esto solo se bifurca dos veces para la llamada
tempfile
.
Errores:
Falta un buen manejo de errores en caso de que
tempfile
falle.Esto hace la eliminación habitual
\n
del shell. Véase el comentario encatch_read()
.No puede usar descriptor de archivo
66
para canalizar datos a su comando. Si necesita eso, use otro descriptor para la redirección, como42
(tenga en cuenta que los shells muy antiguos solo ofrecen FDs hasta 9).Esto no puede manejar bytes NUL (
$'\0'
) enstdout
ystderr
. (NUL es simplemente ignorado. Para la varianteread
se ignora todo lo que hay detrás de un NUL.)
PARA su información:
- Unix nos permite acceder a los archivos eliminados, siempre y cuando mantenga alguna referencia a ellos (como un manejador de archivos abierto). De esta manera podemos abrirlos y luego eliminarlos.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:48
Si el comando 1) sin efectos secundarios con estado y 2) es computacionalmente barato, la solución más fácil es ejecutarlo dos veces. He utilizado principalmente esto para el código que se ejecuta durante la secuencia de arranque cuando aún no sabes si el disco va a funcionar. En mi caso, era un pequeño some_command
por lo que no hubo un golpe de rendimiento para correr dos veces, y el comando no tuvo efectos secundarios.
El principal beneficio es que es limpio y fácil de leer. Las soluciones aquí son bastante inteligentes, pero odiaría ser el que tiene que mantener un script que contenga las soluciones más complicadas. Recomendaría el enfoque simple de ejecutar dos veces si su escenario funciona con eso, ya que es mucho más limpio y más fácil de mantener.
Ejemplo:
output=$(getopt -o '' -l test: -- "$@")
errout=$(getopt -o '' -l test: -- "$@" 2>&1 >/dev/null)
if [[ -n "$errout" ]]; then
echo "Option Error: $errout"
fi
Nuevamente, esto solo está bien porque getopt no tiene efectos secundarios. Sé que es seguro para el rendimiento porque mi código de padres llama a esto menos de 100 veces durante todo el programa, y el usuario nunca notará 100 llamadas getopt vs 200 llamadas getopt.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-02 04:14:58
Aquí hay una variación más simple que no es exactamente lo que el OP quería, pero es diferente a cualquiera de las otras opciones. Puede obtener lo que quiera reorganizando los descriptores de archivo.
Comando de prueba:
%> cat xx.sh
#!/bin/bash
echo stdout
>&2 echo stderr
Que por sí mismo hace:
%> ./xx.sh
stdout
stderr
Ahora, imprima stdout, capture stderr en una variable y registre stdout en un archivo
%> export err=$(./xx.sh 3>&1 1>&2 2>&3 >"out")
stdout
%> cat out
stdout
%> echo
$err
stderr
O registre stdout y capture stderr en una variable:
export err=$(./xx.sh 3>&1 1>out 2>&3 )
%> cat out
stdout
%> echo $err
stderr
Entiendes la idea.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-05 01:28:25
Una solución alternativa, que es hacky pero quizás más intuitiva que algunas de las sugerencias de esta página, es etiquetar los flujos de salida, fusionarlos y dividirlos después en función de las etiquetas. Por ejemplo, podríamos etiquetar stdout con un prefijo "STDOUT":
function someCmd {
echo "I am stdout"
echo "I am stderr" 1>&2
}
ALL=$({ someCmd | sed -e 's/^/STDOUT/g'; } 2>&1)
OUT=$(echo "$ALL" | grep "^STDOUT" | sed -e 's/^STDOUT//g')
ERR=$(echo "$ALL" | grep -v "^STDOUT")
```
Si sabe que stdout y/o stderr son de forma restringida, puede crear una etiqueta que no entre en conflicto con su contenido permitido.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-02 21:19:54
ADVERTENCIA: NO (todavía?) ¡TRABAJANDO!
Lo siguiente parece una posible pista para que funcione sin crear archivos temporales y también solo en POSIX sh; sin embargo, requiere base64 y debido a la codificación/decodificación puede no ser tan eficiente y usar también memoria "más grande".
- Incluso en el caso simple, ya fallaría, cuando la última línea stderr no tiene nueva línea. Esto se puede arreglar al menos en algunos casos reemplazando exe por " { exe; echo > & 2;}", es decir, agregando un nueva línea.
-
El principal problema es, sin embargo, que todo parece picante. Intenta usar un exe como:
Exe() { cat / usr/share/hunspell / de_DE.dic cat / usr/share/hunspell / en_GB.dic > & 2 }
Y verás que, por ejemplo, partes de la línea codificada base64 está en la parte superior del archivo, partes al final, y las cosas stderr no decodificadas en el medio.
Bueno, incluso si la idea de abajo no puede funcionar (lo cual asumo), puede servir como un anti-ejemplo para las personas que pueden creer falsamente que se podría hacer que funcione así.
Idea (o anti-ejemplo):
#!/bin/sh
exe()
{
echo out1
echo err1 >&2
echo out2
echo out3
echo err2 >&2
echo out4
echo err3 >&2
echo -n err4 >&2
}
r="$( { exe | base64 -w 0 ; } 2>&1 )"
echo RAW
printf '%s' "$r"
echo RAW
o="$( printf '%s' "$r" | tail -n 1 | base64 -d )"
e="$( printf '%s' "$r" | head -n -1 )"
unset r
echo
echo OUT
printf '%s' "$o"
echo OUT
echo
echo ERR
printf '%s' "$e"
echo ERR
Da (con la corrección stderr-newline):
$ ./ggg
RAW
err1
err2
err3
err4
b3V0MQpvdXQyCm91dDMKb3V0NAo=RAW
OUT
out1
out2
out3
out4OUT
ERR
err1
err2
err3
err4ERR
(Al menos en dash y bash de Debian)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-27 18:13:42