skip to Main Content

Using Ubuntu 22.04. I have a Bash command that produces a list of GIF files

$ <command>
file1.gif
file3.gif
file12.gif
...

I would like to convert the files in this output to PNG files using the ImageMagick convert command. For a single file, the command would look something like

convert "<basename>.gif" "<basename>.png"

Since I have many files, though, I want to pipe the output of the original command into the convert command, like

$ <command> | convert "<???>" "[<???>%.gif].png"

where I’m attempting to use Bash’s features for removing the .gif extension from input files and adding the .png extension.

Ideally, I could capture the output of each line of <command> as a variable and reference that variable in the convert portion of the command, like

$ <command> | convert "$1" "[$1%.gif].png"

where $1 stands in for file1.gif, file3.gif, file12.gif, etc. I’ve been searching for hours, and I can’t find anything similar to that ability, though.

This is a specific example of a general Bash problem of how to pipe to a command that has two inputs, which does not have an obvious solution because the "destination" of the pipe is ambiguous. I’ve also searched for solutions to that general problem, but fruitlessly.

Can this "one -> multiple" piping be done from the command line?

3

Answers


  1. Consider a BashFAQ #1 while read loop, using a process substitution to avoid the bugs described in BashFAQ #24.

    #!/usr/bin/env bash
    #              ^^^^- this code uses bash extensions and is not sh-compatible
    
    yourcommand() {
      printf '%sn' file1.gif file3.gif etc.gif
    }
    
    while IFS= read -r infile; do
      convert "$infile" "${infile%.*}.png"
    done < <(yourcommand)
    

    If you care more about portability than the bugs described behind the link, then you can pipe to the loop:

    #!/bin/sh
    
    yourcommand | while IFS= read -r infile; do
      convert "$infile" "${infile%.*}.png"
    done
    
    Login or Signup to reply.
  2. Not at a computer to test until tomorrow, but the ImageMagick suite can read its input filenames from a file preceded by @, so if filelist.txt contains 2 filenames, you can append the images side-by-side using:

    convert @filelist.txt +append output.png
    

    If you want to read filenames from ImageMagick‘s stdin, you can use:

    printf "%sn" file1.png file2.png | convert @- +append output.png
    

    But you want multiple output files, so you want mogrify, so you need:

    xargs ... | magick mogrify -format PNG @-
    

    Or… completely different concept… you can get them all done in parallel with GNU Parallel and let it strip the GIF extension and replace it with PNG:

    xargs ... | parallel magick {} {.}.png
    
    Login or Signup to reply.
  3. You can do that in Imagemagick as follows as an example. I have two images lena.jpg and zelda1.jpg and I want to convert them to GIFs.

    echo "lena.jpg zelda1.jpg" | convert @- +adjoin -set filename:f "%t" "%[filename:f].gif"
    

    The @- takes a list from stdin which in this case is just an echo of the file names. The +adjoin is needed to avoid having the image go into different frames of the GIF. (Remove the +adjoin if you want to make and animated GIF). With the +adjoin, you get one output per input. The -set filename:f stores the names from %t which excludes the suffix. They are then recalled in the output as %[filename:f]. Change f to anything you want or just keep f.

    Note: the @- may need you to modify your policy.xml file to be able to use @.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search