Se al tuo comando non importa quanto velocemente gli dai input e non hai davvero bisogno di interagire con esso, allora puoi usare un heredoc.
Esempio:
#!/bin/bash
prog <<EOD
cmdx
save filex
cmdy
save filey
q
EOD
Se hai bisogno di ramificazioni basate sull'output del programma, o se il tuo programma è sensibile alla tempistica dei tuoi comandi, allora Expect è quello che vuoi.
Ti consiglio di usare Expect. Questo strumento è progettato per automatizzare le applicazioni shell interattive.
Dove c'è bisogno, c'è un modo! Penso che sia una buona lezione di bash per vedere come funzionano la gestione dei processi e ipc. La soluzione migliore è, ovviamente, Expect. Ma la vera ragione è che le pipe possono essere complicate e molti comandi sono progettati per attendere i dati, il che significa che il processo diventerà uno zombi per ragioni che potrebbero essere difficili da prevedere. Ma imparare come e perché ci ricorda cosa sta succedendo sotto il cofano.
Quando due processi si impegnano in una conversazione, il pericolo è che uno o entrambi cerchino di leggere dati che non arriveranno mai. Le regole di ingaggio devono essere cristalline. Cose come CRLF e la codifica dei caratteri possono uccidere la festa. Fortunatamente, due partner stretti come uno script bash e il suo processo figlio sono relativamente facili da tenere in riga. La cosa più facile da perdere è che bash sta lanciando un processo figlio per quasi ogni cosa che fa. Se riesci a farlo funzionare con bash, sai perfettamente cosa stai facendo.
Il punto è che vogliamo parlare con un altro processo. Ecco un server:
# 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
Ora, lo script che si spera faccia 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
Si noti che non stiamo semplicemente scaricando i comandi SMTP sul server. Controlliamo ogni codice di risposta per assicurarci che tutto sia a posto. In questo caso, le cose non andranno bene e lo script salterà.