Passing Multiple Files as an Argument to Execute Shell Script

I'm trying to pass multiple files whose paths include spaces to a command line utility (any command line utility). My Keyboard Maestro path and my macOS path are equivalent. And the string I construct with the attached macro works in Terminal.

But run from the macro I get" "Task failed with status 12."

The attached macro is just one of many syntax variants I've tied, thanks to suggestions from @peterlewis and @cstone in other posts on the subject, but nothing is working. Particularly my own noggin.

What am I doing wrong?

Zip file list.kmmacros (4.2 KB)

Quoting is frequently an issue when trying to pass arguments, and vitally important when paths may have spaces in them.

The docs for the Execute a Shell Script action has a long, long section about quoting.

The part you want is near the bottom of that section:

For the reverse problem, where you have a variable that contains multiple parameters, note that you cannot use quotes within a variable - by the time the shell is expanding variables, it has already processed quotes and will not do so again so quotes will just be regular characters passed to the target command and almost certainly result in errors. If possible, use seperate variables for seperate parameters, but if you cannot do that (for example you have a list of paths), store them as seperate lines in a variable (without any quoting or backslashes) and use a command like:

echo "$KMVAR_files" | tr '\r\n' '\0' | xargs -0 ls -l

The tr command will replace \r or \n line endings with a nul character, and the xargs -0 command will read that, split the arguments at the nul character, and pass them to the specified command (in this case ls -l. Note that xargs has a limit to the number of arguments it will pass, so for large numbers of arguments it may run the command multiple times with subsets of the arguments - read the xargs man page for more details.

3 Likes

In my desperate thrashing about, I did see that (in a forum entry of yours) but didn't implement it correctly.

So here's the functioning zip macro:

Zip file list.kmmacros (4.2 KB)

But the zip macro doesn't quite demonstrate the whole problem, it turns out. I'm trying to access these multiple files as @ARGV in a Perl script (which works fine outside Keyboard Maestro).

Here's an small example Perl script that demonstrates the issue using the same same macro as the zip but with this command line:

echo "$KMVAR_localFiles" | tr '\n' '\0' | perl ~/Documents/my\ Tools/Perl\ Scripts/argcounter.pl 
#!/usr/bin/env perl

my $total = $#ARGV + 1;
my $counter = 1;
 
print "ARGV Test\n\n";

# get script name
my $scriptname = $0;
 
print "Total args passed to ${scriptname}: $total\n";
 
foreach my $a (@ARGV) {
	print "Arg ${counter}: $a\n";
	$counter++;
}
print "\nDone";

That reports:

Total args passed to /Users/mrpasini/Documents/my Tools/Perl Scripts/argcounter.pl: 0

So what worked for zip isn't working for argcounter.pl.

This is missing the xargs -0 part.

Should be:

echo "$KMVAR_localFiles" | tr '\n' '\0' | xargs -0 perl ~/Documents/my\ Tools/Perl\ Scripts/argcounter.pl 

read the man page on xargs - it is the part that reads the stdin for the parameters and puts them on the command line.

Ah, thanks, that takes care of it. I must be going blind! Much appreciated, Peter.

1 Like