Why does the zmv command in this script fail?

Contributor II

The end game is to make this a self service item but I can't even get it to work right when run locally.

If I run the zsh zmv command in the interactive shell, it works perfectly. Whatever folder I apply the command to it fixes all illegal characters. But in the script below, zmv fails and it fails silently. No characters are substituted in any folder/file names.

Also, if I change the script to first cd to $FOLDER, the zmv command does it's job. But it doesn't fix illegals in the name of $FOLDER--just all child files and folders...I want it to work the way it does in the interactive shell.

 1 #!/bin/bash
 2 USER="$(stat -f%Su /dev/console)" 
 3 #logged in user chooses a folder 
 4 FOLDER=$(sudo -u $USER -H /usr/bin/osascript << EOD
 5 tell application "System Events"
 6 activate
 7 set FolderName to POSIX path of (choose folder with prompt "Please choose a folder to sanitize")
 8 end tell
 9 EOD)
10 #now that a folder has been chosen, we run  zmv command to  remove, by brute force, all spaces, slashes and other illegals and replace them with underscore
11 /bin/zsh -c "autoload zmv && zmv  '(**/)(*)' '$1${2//[^A-Za-z0-9.]/_}'" $FOLDER
12 osascript -e 'display notification "Your  Folder has been sanitized" with title "JAMF Management Notification"'
13 exit 0

Contributor II

Oh just in case someone asks: why didn't I write it in zsh to begin with?

I tried, but the osascript part failed.

Contributor II


Contributor II

bump again.

Valued Contributor III


I reworked the entire script so it runs entirely within zsh and uses more modern techniques.


# Folder Name Sanitiser
# Original: jamf nation user - teodle
# Modified: Richard Purves (franton) - 16th November 2019

# Set the Jamf Management Action app location
ma="/Library/Application Support/JAMF/bin/Management Action.app/Contents/MacOS/Management Action"

# Find the current console user
currentuser=$( scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }' )

#logged in user chooses a folder 
folder=`sudo -u $currentuser -H /usr/bin/osascript << EOD
tell application "System Events"
set FolderName to POSIX path of (choose folder with prompt "Please choose a folder to sanitize")
end tell

# The choice has been made.
# Use zmv command to remove all spaces, slashes and other illegals and replace them with underscore
cd "$folder" ; autoload zmv && zmv '(**/)(*)' '$1${2//[^A-Za-z0-9.]/}'

# Notify the user that we're done
"$ma" -title "JAMF Management Notification" -message "Your Folder has been successfully sanitized"

1. The script now runs entirely within zsh rather than shelling out from bash. That improves response time.
2. We're using "the new hotness" for finding the current console user. This is far more reliable than your code, which can trip up over fast user switching and multiple users.
3. I've recoded the folder osascript code to be more reliable. The original in brackets didn't execute for me, but backticks made it work with the unix heredoc.
4. Since we're in zsh we don't have to shell out. Instead we "cd" to the chosen folder and then execute the zmv command to do all the renaming required.
5. Finally I replaced the final osascript for notification with the Jamf management action binary, as this works more reliably and looks like it ran in Self Service.

Hope that helps!

Contributor II

Thanks I certainly didn't expect someone to rewrite the script for me!!. If I ever make it to JNUC your money's no good at the bar/coffeeshop for at least one or two rounds. However, my script did actually work if I cd $FOLDER like your script does. I need to be able to also strip out illegals (if they exist) in the dirname of $FOLDER itself. If I run this command directly in interactive shell and specify a folder (either directly or call a variable I defined for the shell session, it changes the foldername itself plus drills down recursively.

/bin/zsh -c "autoload zmv && zmv  '(**/)(*)' '$1${2//[^A-Za-z0-9.]/_}'" $FOLDER

So for example, if $FOLDER is "!@##all my files 2/25/19" it will fix that name as well as drill down recursively.

But in a script I have to cd to the chosen folder just like you did in order to get it to work, and then it doesn't change the name of the folder that the user can choose. I want to make this idiot-proof. The instructions are "pick a folder that you want to move inside your OneDrive Folder or other cloud sync folder. Run this self-service app to remove illegal characters in the chosen folder name and all nested subfolder and files names."

I have another script in production now that gets the logged in user (the old way), cds into their OneDrive sync folder and uses that same zmv syntax and it works great--it doesn't require any interaction because the OneDrive Folder is usually at the same path location--the root of the homedir. That script I was able to write in pure zsh because it doesn't use any osascript. The interactive script is for edge cases where someone changed the default OneDrive Sync folder. On setup, it does let you do that if you want.

But your method of getting logged in user is definitely new. My method is the one (I thought) was the latest accepted conventional wisdom from JAMF nation. But there's always a new and better way and I love learning new stuff!

So again thanks.

Contributor II

I ended up abandoning the idea of letting the end user choose a folder and run zmv substitution. The final solution is to run the script on their OneDrive folder only, because zmv is sooo powerful and fast and  most characters that are  illegal in OneDrive sync folder are just fine anywhere else on a Mac filesystem.