Search code examples
bashpipelineherestring

Using builtin read with pipeline and here strings


I'm processing a package of pictures, from which "file" returns the following:

$ file pic.jpg
pic.jpg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 96x96, segment length 16, baseline, precision 8, 231x288, frames 3
$ file pic.jpg | cut -d',' -f8 | tail -c+2
231x288

So I'm picking dimensions in two variables, using built-in "read", before proceeding with cropping.

But something eludes me. Why isn't this construct working...

$ ( file pic.jpg | cut -d',' -f8 | tail -c+2 | IFS=x read width height ; echo w:$width h:$height; )
w: h:

...while this construct is working?

$ ( IFS=x read width height <<< $(file pic.jpg | cut -d',' -f8 | tail -c+2) ; echo w:$width h:$height; )
w:231 h:288

To summarize, why can't I use a pipeline with built-in "read" in that situation?


Solution

  • In bash, the commands in a pipeline are run in subshells (see the last paragraph of Pipelines in the manual). Any variables you declare in a subshell will disappear when the subshell exits.

    You can use the { } grouping construct to keep the read and the echo in the same subshell:

    file pic.jpg | cut -d',' -f8 | tail -c+2 | { IFS=x read width height ; echo w:$width h:$height; }
    

    This is why the here-string is useful: it runs the read command in the current shell, so the variables are available in the next command.