2

I want a script that can take a list of commands, execute them one-by-one, and show the output. The catch is that some of the commands will launch new shells, which should affect subsequent commands. I have some sample input in a file called commands.

echo "current shell: $0"
ksh
echo "current shell: $0"
exit
echo "current shell: $0"

I have this script, test3.sh which uses a named pipe to send the commands.

#! /usr/bin/env bash

# Create a pipe for processing commands
rm -f shell-pipe
mkfifo shell-pipe

# Set up a bash shell to process the commands sent to the pipe
(tail -f shell-pipe | bash) &

while IFS='' read -r command; do
  echo ">>> sending command: $command"
  echo $command > shell-pipe
  sleep 1
done

# Close the pipe and delete it
fuser -TERM -k shell-pipe > /dev/null
rm shell-pipe

It works.

$ ./test3.sh < commands
>>> sending command: echo "current shell: $0"
current shell: bash
>>> sending command: ksh
>>> sending command: echo "current shell: $0"
current shell: ksh
>>> sending command: exit
>>> sending command: echo "current shell: $0"
current shell: bash

But I don't know in advance how long a command will take to complete, so I need some way to find out if the command is completed yet. So I set up a separate named pipe for the response.

$ cat test4.sh
#! /usr/bin/env bash

# Create a pipe for processing commands
rm -f shell-pipe response-pipe
mkfifo shell-pipe response-pipe

# Set up a bash shell to process the commands sent to the pipe
(tail -f shell-pipe | bash > response-pipe) &

while IFS='' read -r command; do
  echo ">>> sending command: $command"
  echo $command > shell-pipe
  echo "...waiting for response..."
  echo "<<< reponse: " $(cat response-pipe)
done

# Close the pipe and delete it
fuser -TERM -k shell-pipe > /dev/null
rm shell-pipe response-pipe

Unfortunately, this hangs, and eventually I have to use Ctrl-c.

$ ./test4.sh < commands
>>> sending command: echo "current shell: $0"
...waiting for response...
^C

I've experimented with using the exec command, but that didn't help. I used bash in this example, but would be happy to use another shell, or Python, or Haskell.

1 Answer 1

5

That would work if you ran it as bash < file (or any shell that takes care to read its input one line at a time):

$ cat file
echo "current shell: $0"
ksh
echo "current shell: $0"
exit
echo "current shell: $0"
$ bash < file
current shell: bash
current shell: ksh
current shell: bash
$ zsh < file
current shell: zsh
current shell: ksh
current shell: zsh

(doesn't work with dash which reads the whole file (a 8KiB block at least) before starting to interpret what it has read (without doing the seeking back that bash or ksh do), so ksh has nothing left to read when it starts).

Note that if any invoked command (not just shells) read their stdin, they will read from the script as well, so for instance on

export VAR=before
echo "$0 $VAR"
ksh
echo "$0 $VAR"
read VAR
after
echo "$0 $VAR"
exit
echo "$0 $VAR"

You'd get:

bash before
ksh before
ksh after
bash before

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.