How to fix this Automator script?
April 9, 2016 9:35 AM   Subscribe

OS X Automator experts! I need help with an Automator script which will renames files to the name of the subfolder that contains them, move the renamed file to the main folder, and delete the empty subfolder. I've got it partially working but need help fixing it...

I'm trying to create a Folder Action which watches a Downloads folder that often looks like this:

Downloads
--Subfolder1
----randomfile1.txt

--Subfolder2
----randomfile2.txt

I'd like the files in each subfolder to be renamed to their subfolder base name, moved into the main downloads folder, and for the subfolder to be deleted. So at the end the directory should look like this:

Downloads
--subfolder1.txt
--subfolder2.txt

Using this post as a guide, I cobbled together something that sort of works. It renames the file as long as there's only one subfolder in the directory, but when there's two, it balks. Also it doesn't do the file move and subfolder delete thing yet. Here's what I have:

Get specified Finder Items - set to the path while I test this before I make it a folder action

Get folder contents - to get the list of everything there

Filter Finder Items - "All of the following are true - Kind is folder" - to filter out the files and just get the subfolder names

Set Value of Variable "path" - this will be the path to the subfolder

Run Shell Script - set the Pass input : as argument. And use /usr/bin/basename "$1" as the command line to get the subfolder name instead of the full path

Set Value of Variable "subfoldername" - this will be what the file should be renamed to

Get Value of Variable "path" + set action to ignore input from the above action - [I don't understand this step]

Get Folder Contents - get the contents of the folder at the path

Rename Finder Items - name single item, name: basename only to variable "subfoldername"


Since it's pulling all the file names with "get folder contents" I'm guessing it doesn't know which one to rename when there are multiple folders/files. Any ideas how I can fix this so I can do each file and folder in the directory?

Or should I switch to applescript or Hazel to process these files? Thanks for any help you can provide.
posted by bluecore to Computers & Internet (5 answers total)
 
If you already own Hazel, I'd switch to that. It's a simple two step rule to accomplish what you want to do:

Condition: Kind is Folder
Action: Run rules on folder contents

Condition: Kind is Text
Action: Rename with pattern [source folder][extension]
Action: Move to enclosing folder
posted by roosterboy at 10:12 AM on April 9, 2016


Best answer: Sorry, little mistake there. The above will cause the renamed files to move out one level again. Do this instead:

Condition: Kind is Folder
Action: Run rules on folder contents

Condition: Kind is Text
Condition: Subfolder Depth is 1
Action: Rename with pattern [source folder][extension]
Action: Move to enclosing folder

That will ensure that when randomfile1.txt and randomfile2.txt get moved out to the Downloads folder, they won't get processed again and moved out to the parent folder of Downloads.
posted by roosterboy at 10:19 AM on April 9, 2016


Response by poster: Sounds like I should switch to Hazel. Thank you for the info on how to do it!
posted by bluecore at 5:36 PM on April 9, 2016


Best answer: I don't know Automator but I do speak shell script, and if you can pass the name of the overall containing folder as an argument to a shell script fragment that reads
find "$1" -mindepth 2 -maxdepth 2 -print0 |
while IFS= read -rd '' src
do
    dir=${src%/*}
    dst=$dir.${src##*.}
    mv -vn "$src" "$dst"
    rmdir -v "$dir"
done
it should do what you want. If you're going to type that rather than paste it, don't; all the spaces except the ones used for indenting matter. If you need it all on one line, use
find "$1" -mindepth 2 -maxdepth 2 -print0 | while IFS= read -rd '' src; do dir=${src%/*}; dst=$dir.${src##*.}; mv -vn "$src" "$dst"; rmdir -v "$dir"; done
How it works:

The find command emits a list of all files nested two deep inside the folder named by the "$1" argument, which is all that your Automator thing will need to supply. The pipe | at the end of that command causes all its output to flow into the input of the while read loop.

That output is in the form of pathnames separated by nul characters (because of the -print0 action on the find command) and the d '' option on read (note: that's two apostrophes with nothing between them, not a double-quote) makes it expect those nuls rather than the newlines it would ordinarily use. The IFS= prefix to read makes it not strip leading or trailing whitespace from what it reads and the r option tells it not to do anything special with backslash characters; together, these options guarantee that whatever ends up in shell variable src is exactly one of the pathnames emitted by find. The while loop processes these pathnames one at a time.

${src%/*} means "the contents of variable src, with the last slash and everything after it removed". So given a pathname in src like Downloads/Subfolder1/randomfile1.txt, dir=${src%/*} sets variable dir to Downloads/Subfolder1.

${src##*.} means "the contents of variable src, with everything up to and including the last dot removed". If src contains Downloads/Subfolder1/randomfile1.txt, that would be txt and so dst=$dir.${src##*.} would set variable dst to Downloads/Subfolder1.txt.

So now we have $src with the pathname of a file that needs moving, $dst for its post-move destination, and $dir for the pathname of the directory (folder) that will need removing afterwards.

mv -vn "$src" "$dst" moves the file; the n option makes it do nothing if a file with the destination name already exists. Finally, rmdir -v "$dir" removes the containing directory; this will also do nothing if that directory still has files inside, as it will do if the move did nothing or if there was more than one file in it to start with. The v option on both these commands makes them produce some output that says what they did; this might be useful if Automator gives you some way of capturing and display the output from shell scripts, and is harmless if it doesn't.
posted by flabdablet at 1:14 PM on April 10, 2016


Response by poster: Thank you for the shell script info! I appreciate the in-depth explanation of how it works.
posted by bluecore at 3:35 PM on April 10, 2016


« Older Gardening in California: What to do with a DEEP...   |   Poem / reading ideas for a 50th anniversary Newer »
This thread is closed to new comments.