3

I'm in trouble using named pipes (i.e. created with mkfifo) and file descriptors to redirect outputs and grab inputs to/from them within ImageMagick (convert).

Basically, with the following bash code (well, an easier equivalent to what I'm trying) the script hangs and won't show the image:

#!/bin/bash
mkfifo myPipe                              # Create a named pipe
exec 3<>myPipe                             # I/O file descriptor using the pipe
convert rose: -resize 640x480\! png:fd:3   # Write the image to the pipe
convert png:fd:3 win:                      # To read from the pipe using the file descriptor
exec 3>&-                                  # Close the file descriptor
rm myPipe                                  # Close and remove the pipe

Making some research, I saw here this paragraph (it's from Jonathan Leffler's answer):

When a FIFO is opened for reading, it blocks the calling process (normally). When a process opens the FIFO for writing, then the reader is unblocked. When the writer closes the FIFO, the reading process gets EOF (0 bytes to read), and there is nothing further that can be done except close the FIFO and reopen.

To avoid that, I tried to use Mark Edgar's solution consisting of these instructions (code taken from the answer):

# Create pipe and start reader.
mkfifo pipe
cat pipe &
# Open pipe for writing.
exec 3>pipe
echo one >&3
echo two >&3
# Close pipe.
exec 3>&-

Unfortunately, I need to read from the pipe and write to it with file descriptors (i.e. fd:X using convert). How can I accomplish that without the hanging problems?

  • 2
    You probably want an & after convert rose: -resize 640x480\! png:fd:3 – PSkocik Apr 23 '16 at 5:34
  • Still you might need to close the writing ends so that reading can finish. Why don't you simply pipe it like this: convert rose: -resize 640x480\! png:fd:1 | convert png:fd:0 win: and completely get rid of the named pipe? – PSkocik Apr 23 '16 at 5:37
  • @PSkocik First of all, that worked! And for the second, in fact I started using normal pipes, but the commands I need to run are really long, and wanted to split the process without saving temporary images. – user129371 Apr 23 '16 at 5:39
  • In this case, named pipes do exactly the same thing like normal pipes except that they leave stuff in the file system that you later need to rm. If you want to split it, I say use normal pipes and simply spread the commands across multiple lines. Anyway, I'm glad it worked. – PSkocik Apr 23 '16 at 5:44
  • 1
    @PSkocik Great! Well, as my problem is solved, please put what you've told me as an answer (and by the way, could you please include the way to split the commands in multiple lines?) to finish :) – user129371 Apr 23 '16 at 5:46
3

The problem is that while

exec 3<>myPipe

opens both the R and the W end of the FIFO simultaneously, thereby preventing a deadlock there,

convert rose: -resize 640x480\! png:fd:3

is a normal/foreground command that needs to finish before the next command can run. Because it's writing what's likely to be a lot of data to a FIFO, it won't finish, but instead get blocked when the FIFO buffer is full, effectively deadlocking the script.

To prevent that, you need put this command in the background so that the next process can concurrently drain the FIFO, preventing it from filling up.

As I've mentioned in the comment, in this case I'd recommend using regular pipes instead of named pipes:

convert rose: -resize 640x480\! png:fd:1 | 
   convert png:fd:0 win:

If you're worried about your code lines becoming too long, you can create continuation lines by ending lines with \ or by putting newlines after tokens that need a right-hand side (such as ||, &&, or | as I did it) (I also tend to indent my continuation lines for better readability, but that's purely optional).

  • 1
    Excellent explanation! And thank you very much for the splitting lines procedure! :) – user129371 Apr 23 '16 at 6:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy