2010-08-10 14 views
7

Estoy usando un programa de línea de comando interactivo en un terminal Linux que ejecuta el shell bash. Tengo una secuencia definida de comando que ingresé al programa de shell. El programa escribe su salida en salida estándar. Uno de estos comandos es un comando 'guardar', que escribe el resultado del comando anterior que se ejecutó, en un archivo en el disco.¿Es posible hacer que un script bash shell interactúe con otro programa de línea de comando?

Un ciclo típico es:

$prog 
$$cmdx 
$$<some output> 
$$save <filename> 
$$cmdy 
$$<again, some output> 
$$save <filename> 
$$q 
$<back to bash shell> 
  • $ es el indicador bash
  • $$ es rápido
  • q del programa es el comando quit para prog
  • prog es tal que se añade la salida de la orden anterior a nombre

¿Cómo puedo automatizar este proceso? Me gustaría escribir un script de shell que pueda iniciar este programa, y ​​recorrer los pasos, pasarle los comandos uno por uno y luego salir. Espero que el comando save funcione correctamente.

Respuesta

15

Si a su comando no le importa qué tan rápido le da entrada, y realmente no necesita interactuar con con él, entonces puede utilizar un heredoc.

Ejemplo:

#!/bin/bash 
prog <<EOD 
cmdx 
save filex 
cmdy 
save filey 
q 
EOD 

Si necesita una ramificación basándose en la salida del programa, o si su programa es en absoluto sensibles a la frecuencia de sus comandos, a continuación, esperar es lo que quiere.

+0

Eso sirvió a mi propósito. No estaba seguro de dónde iría la salida estándar. – rup

0
echo "cmdx\nsave\n...etc..." | prog 

..?

9

Te recomiendo que uses Expect. Esta herramienta está diseñada para automatizar aplicaciones de shell interactivas.

1

Para los casos de uso simples que usted puede usar una combinación de subnivel, eco & sueño:

# in Terminal.app 
telnet localhost 25 
helo localhost 
ehlo localhost 
quit 

(sleep 5; echo "helo localhost"; sleep 5; echo "ehlo localhost"; sleep 5; echo quit) | 
    telnet localhost 25 
1

utilizo esperan interactuar con la cáscara de conmutadores y enrutadores copias de seguridad. Un script bash llama al script esperado con las variables correctas.

para i in; hacer expect_script.sh $ i; exit

Esto mostrará ssh en cada cuadro, ejecutará los comandos de copia de seguridad, copiará los archivos apropiados y luego pasará al siguiente cuadro.

6

¡Donde hay una necesidad, hay una manera! Creo que es una buena lección bash ver cómo funciona la gestión de procesos y ipc. La mejor solución es, por supuesto, esperar. Pero la verdadera razón es que las tuberías pueden ser complicadas y muchos comandos están diseñados para esperar los datos, lo que significa que el proceso se convertirá en un zombie por razones que la bahía es difícil de predecir.Pero aprender cómo y por qué nos recuerda lo que está pasando bajo el capó .

Cuando dos procesos entablan una conversación, existe el peligro de que uno o ambos intenten leer datos que nunca llegarán . Las reglas de enfrentamiento deben ser claras como el cristal. Cosas como CRLF y codificación de caracteres pueden matar a la fiesta. Afortunadamente, dos socios cercanos como un script bash y su proceso hijo son relativamente fáciles de mantener en línea. Lo más fácil de perder es que bash es al iniciar un proceso hijo para casi todo lo que hace. Si puede hacer que trabaje con bash, sabrá a fondo lo que está haciendo.

El punto es que queremos hablar con otro proceso. Aquí hay un servidor:

# a really bad SMTP server 

# a hint at courtesy to the client 
shopt -s nocasematch 

echo "220 $HOSTNAME SMTP [$$]" 

while true 
do 
    read 
    [[ "$REPLY" =~ ^helo\ [^\ ] ]] && break 
    [[ "$REPLY" =~ ^quit ]] && echo "Later" && exit 
    echo 503 5.5.1 Nice guys say hello. 
done 

NAME=`echo "$REPLY" | sed -r -e 's/^helo //i'` 
echo 250 Hello there, $NAME 

while read 
do 
    [[ "$REPLY" =~ ^mail\ from: ]] && { echo 250 2.1.0 Good guess...; continue; } 
    [[ "$REPLY" =~ ^rcpt\ to: ]] && { echo 250 2.1.0 Keep trying...; continue; } 
    [[ "$REPLY" =~ ^quit ]] && { echo Later, $NAME; exit; } 
    echo 502 5.5.2 Please just QUIT 
done 

echo Pipe closed, exiting 

Ahora, el guión que con suerte hace la magia.

# Talk to a subprocess using named pipes 

rm -fr A B  # don't use old pipes 
mkfifo A B 

# server will listen to A and send to B 
./smtp.sh <A> B & 

# If we write to A, the pipe will be closed. 
# That doesn't happen when writing to a file handle. 
exec 3>A 

read < B 
echo "$REPLY" 

# send an email, so long as response codes look good 
while read L 
do 
    echo "> $L" 
    echo $L > A 
    read < B 
    echo $REPLY 
    [[ "$REPLY" =~ ^2 ]] || break 

done <<EOF 
HELO me 
MAIL FROM: me 
RCPT TO: you 
DATA 
Subject: Nothing 

Message 
. 
EOF 

# This is tricky, and the reason sane people use Expect. If we 
# send QUIT and then wait on B (ie. cat B) we may have trouble. 
# If the server exits, the "Later" response in the pipe might 
# disappear, leaving the cat command (and us) waiting for data. 
# So, let cat have our STDOUT and move on. 
cat B & 

# Now, we should wait for the cat process to get going before we 
# send the QUIT command. If we don't, the server will exit, the 
# pipe will empty and cat will miss its chance to show the 
# server's final words. 
echo -n > B  # also, 'sleep 1' will probably work. 

echo "> quit" 
echo "quit" > A 

# close the file handle 
exec 3>&- 

rm A B 

Tenga en cuenta que no estamos simplemente abandonando los comandos SMTP en el servidor. Verificamos cada código de respuesta para asegurarnos de que todo esté bien. En este caso, las cosas no serán OK y la secuencia de comandos se bloqueará.

Cuestiones relacionadas