2012-05-14 18 views
10

Estoy tratando de usar bash para abrir un nuevo descriptor para escribir mensajes de diagnóstico adicionales. No quiero usar stderr, porque stderr solo debe contener el resultado de los programas invocados por bash. También quiero que mi descriptor personalizado sea redireccionable por el usuario.En bash, ¿cómo abro un descriptor de archivo modificable que se puede redireccionar externamente?

yo probamos este:

exec 3>/dev/tty 
echo foo1 
echo foo2 >&2 
echo foo3 >&3 

Pero cuando intento volver a dirigir fd 3, la salida sigue escribiendo en el terminal.

$ ./test.sh >/dev/null 2>/dev/null 3>/dev/null 
foo3 
+0

Un sitio útil [link] (http://www.linuxtopia.org/online_books/advanced_bash_scripting_guide/x13082.html) – Mike

+0

Usted puede ser capaz de utilizar una [nombre de tubería] (http: // Linux. die.net/man/1/mkfifo). –

Respuesta

5

bastante simple: Si el shell padre es no redireccionamiento fd 3, entonces habrá test.sh redirigir fd 3 a /dev/tty.

if ! { exec 0>&3; } 1>/dev/null 2>&1; then 
    exec 3>/dev/tty 
fi 
echo foo1 
echo foo2 >&2 
echo foo3 >&3 
+0

Por más que quisiera dar la aceptación a gregc, no ha regresado en casi 4 semanas para aclarar su respuesta. Este, aunque probablemente "prestado" de gregc, es el enfoque más simple. – Kelvin

1

actualización

Esto se puede hacer. Vea la respuesta de kaluy de la manera más simple.

respuesta original

Parece que la respuesta es "no se puede". Los descriptores creados en un script no se aplican al shell que invocó el script.

Descubrí cómo hacerlo usando ruby, si alguien está interesado. Ver también la actualización usando perl.

begin 
    out = IO.new(3, 'w') 
rescue Errno::EBADF, ArgumentError 
    out = File.open('/dev/tty', 'w') 
end 
p out.fileno 
out.puts "hello world" 

Tenga en cuenta que esto obviamente no funcionará en un demonio - no está conectado a un terminal.

ACTUALIZACIÓN

Si Ruby no es lo suyo, sólo tiene que llamar a una escritura del golpe del guión rubí. Necesitará el Open4 gema/biblioteca para tuberías fiable de salida:

require 'open4' 

# ... insert begin/rescue/end block from above 

Open4.spawn('./out.sh', :out => out) 

ACTUALIZACIÓN 2

Aquí está una manera con un poco de Perl y sobre todo bash. Debe asegurarse de que Perl funcione correctamente en su sistema, ya que un ejecutable Perl que falta también devolverá un código de salida distinto de cero.

perl -e 'open(TMPOUT, ">&3") or die' 2>/dev/null 
if [[ $? != 0 ]]; then 
    echo "fd 3 wasn't open" 
    exec 3>/dev/tty 
else 
    echo "fd 3 was open" 
fi 
echo foo1 
echo foo2 >&2 
echo foo3 >&3 
6

En primer lugar el descriptor de archivo de conjuntos de concha matriz 3 a/dev/null
A continuación, el programa establece descriptor de archivo de 3 a/dev/tty
Así que sus síntomas no son realmente sorprendentes.

Editar: Usted puede comprobar para ver si fd 3 se ha establecido:

if [[ ! -e /proc/$$/fd/3 ]] 
then 
    exec 3>/dev/tty 
fi 
+0

+1 para la explicación. La solución que eliminaste casi funcionó. Es solo que '-t' solo puede detectar si el fd está conectado a un terminal, no si existe. – Kelvin

+0

Pruebe la nueva sugerencia – cdarke

+1

Solo pensé en la prueba/proc y luego la publicó. Las grandes mentes piensan igual, tal vez. Lamentablemente, esto no es portátil. Funciona muy bien en Linux, pero no en Mac. Daría otro voto si pudiera. – Kelvin

2

He aquí una manera de comprobar si un descriptor de archivo ya ha sido establecido mediante el (Bash) shell solamente.

(
# cf. "How to check if file descriptor exists?", 
# http://www.linuxmisc.com/12-unix-shell/b451b17da3906edb.htm 

exec 3<<<hello 

# open file descriptors get inherited by child processes, 
# so we can use a subshell to test for existence of fd 3 
(exec 0>&3) 1>/dev/null 2>&1 && 
    { echo bash: fd exists; fdexists=true; } || 
    { echo bash: fd does NOT exists; fdexists=false; } 

perl -e 'open(TMPOUT, ">&3") or die' 1>/dev/null 2>&1 && 
    echo perl: fd exists || echo perl: fd does NOT exist 

${fdexists} && cat <&3 
) 
+0

+1 Esto parece ser correcto. Voy a probar un poco más. Pero debes separar la parte perl, porque parece que es parte del guión. Después de leer más de cerca, veo que solo lo estás usando para comparar. Además, no estoy seguro de cuál es el propósito del 'exec 3 <<< hello'; ponerlo antes de la prueba 'fdexists' hará que la prueba sea verdadera todo el tiempo. – Kelvin

+0

He confirmado que esto tiene la idea correcta, pero necesita limpieza. Si puede tomar el guión en mi pregunta y modificarlo con su solución, entonces aceptaré su respuesta. – Kelvin

+0

'perl' parte es una solución alternativa. 'exec 3 <<< hello' hay para probar. Descomentar esta línea tiene el mismo efecto que redirigir el descriptor de archivo desde el exterior. –

1

@Kelvin: Aquí está el script modificado que solicitó (más algunas pruebas).

echo ' 
#!/bin/bash 

# If test.sh is redirecting fd 3 to somewhere, fd 3 gets redirected to /dev/null; 
# otherwise fd 3 gets redirected to /dev/tty. 
#{ exec 0>&3; } 1>/dev/null 2>&1 && exec 3>&- || exec 3>/dev/tty 
{ exec 0>&3; } 1>/dev/null 2>&1 && exec 3>/dev/null || exec 3>/dev/tty 

echo foo1 
echo foo2 >&2 
echo foo3 >&3 

' > test.sh 

chmod +x test.sh 

./test.sh 
./test.sh 1>/dev/null 
./test.sh 2>/dev/null 
./test.sh 3>/dev/null 
./test.sh 1>/dev/null 2>/dev/null 
./test.sh 1>/dev/null 2>/dev/null 3>&- 
./test.sh 1>/dev/null 2>/dev/null 3>/dev/null 
./test.sh 1>/dev/null 2>/dev/null 3>/dev/tty 

# fd 3 is opened for reading the Here String 'hello' 
# test.sh should see that fd 3 has already been set by the environment 
# man bash | less -Ip 'here string' 
exec 3<<<hello 
cat <&3 
# If fd 3 is not explicitly closed, test.sh will determine fd 3 to be set. 
#exec 3>&- 
./test.sh 

exec 3<<<hello 
./test.sh 3>&- 
+0

Esto se colocaría mejor en su respuesta original editándolo. –

+0

Además, debe usar su cuenta SO original en lugar de una nueva. – Kelvin

+0

Lo siento, esto no funciona. Si ejecuto el script, redirigiendo fd 3 a un archivo, no se escribe nada. – Kelvin

Cuestiones relacionadas