'For each item' and Finder selections

I’m having both success and failures with the “For each item in a collection” when used with a Finder selection and then trying to use a shell script.

Example 1, and this one works: For each item in a collection, set variable = FullFilePath, based on The Finder’s selection, then execute: shell script of type text script, where the script is just this:

gzip -d -k $KMVAR_FullFilePath

That works exactly as I planned: I select a number of .gz files, call the macro, and they all expand.

But I wanted to modify this to handle files with different compression, i.e. I could select both .zip and .gz files, and decompress them in one pass. I wrote and tested a shell script, and it’s working fine in the shell. But the way I wrote it, I was pretty sure it wouldn’t work in KM - I create an array of filenames based on what’s passed on the command line, i.e. “uncompress *” grabs all the filenames in the directory, adds them to an array, then processes each in a ‘for’ loop.

For KM, which (I believe) runs the program over and over, once for each match, i wrote a much simpler version that just takes the first parameter and runs that through a case statement to figure out how to expand. It looks like this:

#!/bin/bash

OLDIFS=$IFS
IFS=$'\n'

echo $1
(for debugging)

case $1 in
	*.tar.bz2)  tar xjf $1       ;;
	*.tar.gz)   tar xzf $1       ;;
	*.bz2)      bunzip2 $1       ;;
	*.gz)       gunzip $1        ;;
	*.tar)      tar xf $1        ;;
	*.tbz2)     tar xjf $1       ;;
	*.tgz)      tar xzf $1       ;;
	*.zip)      unzip $1         ;;
	*.Z)        uncompress $1    ;;
	*.rar)      rar x $1         ;;  # 'rar' must to be installed
	*.jar)      jar -xvf $1      ;;  # 'jdk' must to be installed
	*)          echo "'$1' cannot be extracted via extract()" ;;
esac

IFS=$OLDIFS`

When I test this in Terminal, it works fine: Whatever file I indicate will be decompressed. in KM, I set this one up as “Execute script file,” since it’s a multi-line thing. I set it up like this:

/path/to/simple/script $KMVAR_FullFilePath

If I run that version, I get Execute Shell Script Exception as the only output. So then I tried without specifying the variable, and I get an error indicating that there’s no string value passed to the script.

What (obvious, I’m sure) thing am I missing here? I’ve read the help, the wiki, searched the net … but I’m stumped.

thanks;
-rob.

What appears in the Engine.log file?

You are not defending against having spaces in the path, so if your files ever have spaces anywhere in their path you are going to have problems. Better is:

gzip -d -k "$KMVAR_FullFilePath"

I am afraid I don’t know much bash programming to tell you what is going wrong.

If you remove everything except the first line and the echo, and if the Execute Shell Script is set to display the result in a text window, do you get the paths correctly displayed in windows?

Sorry for the confusion - I actually added the quotes on the gzip version, and it’s working fine. The problem is with the other command, which calls a canned script:

/path/to/simple/script $KMVAR_FullFilePath

I’ve tried that with and without the quotes, and get no output either way. All i get with that one is the execution exception. The log file says:

016-12-04 21:02:19 Assertion Failed: tempScriptHolder.shellCommand, file: /Users/peter/Keyboard Maestro/Project/Source/Actions/AExecuteRootScript.mm:44, value: 0
2016-12-04 21:02:19 ERROR: Execute Shell Script Exception

[Edit: I just noticed the path in the error message - why’s it pointing to /Users/peter ?]

If I comment everything but the echo, I get the exact same error with the same entry in the log file. So basically, this command…

/path/to/shell/script/unz “$KMVAR_FullFilePath”

…fails, even if unz is changed to simply echo the entry text. In Terminal, it runs fine:

$ unz Soft\ Gingerbread.pdf.gz
Soft Gingerbread.pdf.gz

But in KM, I get the above exception, regardless of quotes or no quotes around the variable call. With my script edited in this way, what really confuses me is that it’s identical, basically, to the one that works.

Works: gzip -d -k “$KMVAR_FullFilePath” [execute text script]
Doesn’t work: /path/to/shell/script/unz “$KMVAR_FullFilePath” [execute script file]

The only difference is that the broken one has to send the variable to a saved script, the one that works just sends it to the command line. So I tried changing the broken one to a text script, with just the echo, because that’s the only command in the saved script:

echo “$KMVAR_FullFilePath”

When written in that manner, it works fine - the window shows the full path to the file. But if I call a shell script that has the exact same command in it (except it has to be written as “echo $1,” to reflect the second argument in the command) … it fails.

I’m really stumped here.

thanks;
-rob.

It is just a string, it tells me where the error is.

Hmm, of course that won work because

/path/to/shell/script/unz "$KMVAR_FullFilePath"

is not a path to a script file.

You undoubtedly do not have any file named unz "/path/to.whatever/file" in the /path/to/shell/script directory.

What you are doing is a text script, not a script file.

Oh! That was totally my mistake. I thought you could only use text script for a single line command, not for calling a saved script. OK, so I fixed that :).

Now I need to get just the path - without the filename - into the script, so I can provide it some of the commands (because it seems to be running in the top-level folder). I’m thinking I can manipulate the string with regex somehow, so I’ll go futz with that now.

thanks!
-rob.

And that’s that - got it working, thanks to bash’s ‘dirname’ command, which will return the base path to a referenced file. So I set a variable to the working directory with…

workdir=`dirname $KMVAR_FullFilePath`

…and now it works perfectly - thanks!

-rob.

For future reference, the Get File Attribute action can get out the different components of a path including the parent path.

2 Likes

Ah, that’s very useful to know! And a related question … how exactly is this working…

For each item in the collection of the Finder’s selection, I have it run the gzip command:

gzip -d -k “$KMVAR_FullFilePath”

But that variable … how is it storing the path? Is that the only variable associated with the collection? Because I don’t ever tell KM that FullFilePath should hold the path to the file, I just give it a name.

thanks;
-rob.

FullFilePath is specified in the For Each action as the variable to hold the value from the collection (in this case the path from the collection of the Finder Selection).

Right, but I could’ve named the variable Restaurant_at_the_end_of_the_Universe, and it still would contain the path, right? So is path the only value that’s available from a Finder collection? (Sorry, I didn’t ask this very clearly the first time.)

-rob.

Hey Rob,

Yes.  :smile:

You could also omit the underscores and have a valid variable name:

“Restaurant at the end of the Universe”

A lot of people (myself included) don't initially realize that the collection format is:

For each <user defined variable name> in these collections:

For that reason I nearly always use “pathVar” as my variable name in those situations:

(Other appropriate names might be “lineVar” or “substringVar” depending on the type of collection.)

While the way this action functions is quite clear to me after long usage, I want anyone who isn't familiar with it to be able to see immediately that the variable name is a variable and what it is supposed to contain.

-Chris

1 Like

Yes, that’s right - the variable name can be anything you want, that’s up to you. So maybe it might be “Important File” or “File To Trash” or whatever.

Each different collection will return different values. For the Finder Selection, it is a collection of paths to the selected files. For other collections, it might be a collection of strings or a collection of rectangles or whatever.

And for more future reference, the Split Path action is even better at splitting apart a path into its component parts.

1 Like