xdhmoore
xdhmoore

Reputation: 9876

Pipe multiline powershell results into multiple arguments in one line

I'm trying to write a oneliner that gets unique ids of tif files in a directory, and then runs ImageMajick's convert to combine them together into multipage tifs:

convert ..\image_data\ua-nws_00000991_001.tif ..\image_data\ua-nws_00000991_002.tif ..\image_data\ua-nws_00000991_003.tif ..\image_data\ua-nws_00000991_004.tif ua-nws_00000991_all.tif

This works, but I'm trying to automate it for a bunch of ids that have several tifs each. For now, I'm just using one id and using "echo" instead of convert:

echo "ua-nws_00000991" | % { $id=$_; ls ../image_data | ? { $_.FullName -match $id } | % { 
    echo $_.FullName } | Join-String -Separator '" "' | % { echo '"'$_'"' $id"_all.tif" }
}

But I'm having trouble turning the list of files into a list of arguments. I'm getting the following (I've manually made the paths relative for online posting):

"
.\image_data\ua-nws_00000991_001.tif" ".\image_data\ua-nws_00000991_002.tif" ".\image_data\ua-nws_00000991_003.tif" "C:\Daniels_Stuff\Classes\Current\grad_ai\project\repo\article-reconstruction\image_data\ua-nws_00000991_004.tif"
ua-nws_00000991_all.tif

Before I added the double quotes, convert was interpreting all of the final $_ as one argument. I'm adding quotes in order to force convert to see them as multiple arguments. But I can't seem to get rid of that newline. I think it might be caused by the final echo, but if I replace it with convert, I get the following:

convert.exe: unable to open module file `C:\Program Files\ImageMagick-6.9.2-Q16\modules\coders\IM_MOD_RL_\ABSOLUTE\PATH\TO\MY\IMAGES\IMAGE_DATA\UA-NWS_00000991_001.TIF C_.dll': No such file or directory @ warning/module.c/GetMagickModulePath/674.
convert.exe: no decode delegate for this image format `\ABSOLUTE\PATH\TO\MY\IMAGES\IMAGE_DATA\UA-NWS_00000991_001.TIF C' @ error/constitute.c/ReadImage/501.
convert.exe: no images defined `ua-nws_00000991_all.tif' @ error/convert.c/ConvertImageCommand/3241.

Upvotes: 0

Views: 761

Answers (3)

Mark Setchell
Mark Setchell

Reputation: 207445

Whilst I don't understand Powershell's mad syntax, there are two aspects to ImageMagick that may help you out...

Aspect 1 - Filenames on stdin

Firstly, you can pass a list of filenames to merge into a single TIF on ImageMagick's stdin rather than as parameters. So, if you have 3 pages of a book, in 3 files called p1.tif, p2.tif and p3.tif, rather than do:

convert p*.tif book.tif

you can also do

dir /b/s p*tif | convert @- book.tif

Whilst I am generating the names above using DIR /B/S, I expect you would want to replace that with your Powershell commands.

Aspect 2 - Multiple images down single pipe

Secondly, you can pipe multiple images from multiple invocations of convert into a further final invocation of convert using "Magick Image File Format" (MIFF) streaming:

Here is one example, where two invocations of convert both send an image each to a final convert that joins the images from its MIFF input stream:

( convert p1.tif MIFF:- & convert p2.tif MIFF:- ) | convert MIFF:- book.tif

Here is a slightly different example of the same thing:

FOR files *.TIF; DO
   IF this file has the correct id
      convert $this MIFF:-
   ENDIF
ENDDO | convert MIFF:- book.tif 

Upvotes: 1

TessellatingHeckler
TessellatingHeckler

Reputation: 28983

I don't know where Join-String comes from, but I can only guess that it's broken - because I can't see anywhere else the newline is coming from.

Put some test files in a folder:

Name                      
----                      
ua-nws_00000991_001.tif   
ua-nws_00000991_002.tif   
ua-nws_00000991_003.tif   
ua-nws_00000991_004.tif   
ua-nws_00000992_001.tif   
ua-nws_00000992_002.tif   
ua-nws_00000992_003.tif   
ua-nws_00000992_004.tif   

List them with Get-ChildItem (gci), and group them (Group-Object, aka group) by stripping off the trailing number from the filename:

PS D:\t\New folder> gci *.tif | group { $_.Name -replace '_\d+\.tif$' }

Count Name                      Group                                                                                                       
----- ----                      -----                                                                                                       
    4 ua-nws_00000991           {D:\t\New folder\ua-nws_00000991_001.tif, D:\t\New folder\ua-nws_00000991_002.tif, D:\t\New folder\ua-nws...
    4 ua-nws_00000992           {D:\t\New folder\ua-nws_00000992_001.tif, D:\t\New folder\ua-nws_00000992_002.tif, D:\t\New folder\ua-nws...

Now we have groups, where the group name is the image ID, and the list of files for that ID is the group member. Neat. Now run convert once foreach (%) group by taking the full filenames as a parameter to convert, and generating a new filename from the group id ($_.name) with _full.tif on the end:

gci *.tif | group { $_.Name -replace '_\d+\.tif$' } |% { Start-Process convert -ArgumentList (@($_.group.fullname) + @("$($_.Name)_full.tif"))}

This probably won't work as is, I don't have convert installed to try it, and you'll need to adjust the paths. But I think the approach will work.

Upvotes: 1

Moerwald
Moerwald

Reputation: 11254

You could try the following:

get-childitem "image_data" -recurse -filter ua-nws_00000991*.tif | % { convert $_.Fullname }

Returns all tif files in "image_data" directory and calls convert for every found tif file.

hope that helps

Upvotes: -1

Related Questions