1

I'm just learning bash scripting. It's the first time I have to redirect output to another program and I don't know how to do it.

I have to write a script which connects a GUI program, and zero, one or two programs - I need two players, both can be computer or human. GUI gets output from both programs (or humans, I mean from stdin).

Let's assume that there is one human and one comp_player. Human gives command using stdin, this command has to be redirected to running GUI program and running comp_player, both expecting input. Then, comp_player's output has to be redirected to GUI (if there were second computer player, it would also be necessary to redirect this output to second computer player's input). The turn ends.

I know how to create a file to read and write and redirect input or output from it. For example:

echo "anything" >&3
exec 3<>sometextfile
read line <&3
echo $line

But what I don't know is how to redirect, for example, line I just read to running program who expects input and capture its output, which I can redirect to GUI and another program.

I know it isn't as simple as code above and I that have to use something called named pipes, but I tried to read some tutorials and I failed to write working script.

Can you give me an example of fragment of a script which, say:

(gui program and computer player program are running)

-reads line from stdin

-"sends" the line to gui program's and comp_player's inputs

-"reads" output from comp_player and writes it to stdout and also "sends" it to gui input

1 Answer 1

2

Named pipes are a special kind of files used to connect the input and output of two completely separate programs. Think of it as a temporary buffer, or an array that's shared between two programs that don't know about each other. This makes them an awesome tool to share messages between the two programs and get them to communicate very effectively.

As a simple test to see how a named pipe works, open two terminals in the same directory, and type mkfifo mypipe in the first one to create the file. Now, to use it just write something to it, for example:

echo "A very important message" > mypipe

Now the message is stored in the pipe file, you will see the terminal is blocked, as if the echo hadn't finish. Go to the second terminal and get the contents of the pipe using:

cat mypipe

You will print out the "very important message" you stored in the piped from the first terminal. Notice the pipe is empty now, and you simply can't get the message again from it.


Now that you know how named pipes work, here's a very simple example of how three players would communicate. Notice that we can't use a single file for all of them, instead we will create separate pipes to communicate player1 and player2, player1 and gui, and player2 and gui. I'm guessing the gui program is written in another language, but I will leave that to you.

PLAYER 1 (HUMAN)

player2pipe="pipe1"
guipipe="pipe2"

#First make sure we have our files
if [ ! -p $player2pipe ]; then
    mkfifo $player2pipe
fi

if [ ! -p $guipipe ]; then
    mkfifo $guipipe
fi


while true; do #Or until the game ends
    echo -n "Do something: "
    read move
    # Send our move to the other two players
    echo $move > $player2pipe
    echo $move > $guipipe

    playermove=$(cat $player2pipe) # Read other player's move from the pipe file. The execution will pause until there's something to read

    # Do something about that move here

done

PLAYER2 (COMPUTER)

player1pipe="pipe1"
guipipe="pipe3"


if [ ! -p $player1pipe ]; then
    mkfifo $player1pipe
fi

if [ ! -p $guipipe ]; then
    mkfifo $guipipe
fi


while true; do

    playermove=$(cat $player1pipe)

    # Do something about that move here

    move="A very good move made by a computer" #Obviously you will have to generate a new move
    echo $move > $player1pipe
    echo $move > $guipipe

done

GUI

player1pipe="pipe2"
player2pipe="pipe3"

if [ ! -p $player1pipe ]; then
    mkfifo $player1pipe
fi

if [ ! -p $player1pipe ]; then
    mkfifo $player1pipe
fi


while true; do #Or until the game ends

    # Read other players' move from the pipe files. Notice the order here, if player2 moved before player1 the execution would be locked until the pipe is emptied
    player1move=$(cat $player1pipe)
    player2move=$(cat $player2pipe)

    #Print out their move or whatever you need to do with it.
    echo $player1move
    echo $player2move

    # Do whatever else you need to do about those moves
done


Save the three files in the same directory and execute them from three different terminals to see how they work. Hope I helped.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks a lot for a really comprehensive explanation! Of course gui and AI are independent programs (in C), and I just need do everything in one script, but thanks to your answer I know how the named pipes work and how to realize it.
Glad to help. As a sidenote, I just noticed there's no need to wait for something to be written to the pipe file using that weird while loop, you can just try to read from it, and the execution will pause until something is written to it. So those loops can be removed, if you were using any of them. I'll edit the code for future reference.
Today I started to continue my project. Using pipes as shown above is ok for me. But I still have problem with running a program ex. ./my_prog or two which is not bash script and continuously send text trough a pipes and read from them. For example, I can (I placed pipe in file descriptor 3) echo "something" >&3 then ./my_prog <&3, but then I do echo "something else" >&3 and nothing happens
What should I do to be able to send "something" (it reads it now) to my running program, do something else in my script (program waits) and then send "another thing" as next input of my running program (and it reads it now again)?
You are probably running into errors because echo will close the pipe file after writing something into it, so your external C program will constantly run into an EOF. The only solution that I can think of (I just tested it and it works) is to also close the file descriptor in C once you've read something and then reopen it when you need it again. It's not be the most efficient solution, so you may want to google a way to keep files open in bash to avoid this. If you still want to do it the "slow" way, then something like: while(1) { open(pipe); read(pipe); close(pipe); } should work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.