Shell scripting bat signal: defaults not obeying variable resolution

jarednichols
Honored Contributor

Okay, this may sound a bit odd given my shell scripting experience, but I need help. It's probably something stupid. Here's the script I've got:

#!/bin/ksh

# Filename: mountShares.sh
# Purpose: Mount file shares automatically with Kerberos ticket
# Author: Jared F. Nichols

user=`ls -la /dev/console | cut -d " " -f 4`
server=`dscl . -read /Users/$user | grep SMBHome: | cut -d '' -f 3`

jamf mount -server $server -share $user -type smb

defaults write com.apple.dock persistent-others -array-add '<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>/Volumes/$user</string><key>_CFURLStringType</key><integer>0</integer></dict><key>showas</key><integer>2</integer></dict><key>tile-type</key><string>directory-tile</string></dict>'

killall Dock

The gist is that it will take a kerberos ticket, mount a server share and then put the share in the dock. My issue is that for the life of me I can't get $user to resolve. Defaults puts a literal "$user" in on the dock. I've tried all manner of escaping it, quoting, setting the defaults as another variable (which can get it to resolve properly) and then, essentially, run the variable - all to no effect.

Any ideas?? I should be able to lick this one but it's kicking my arse.

1 ACCEPTED SOLUTION

henkelb
New Contributor III

This also seems to work.

tmpStr='<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>/Volumes/'$user'</string><key>_CFURLStringType</key><integer>0</integer></dict><key>showas</key><integer>2</integer></dict><key>tile-type</key><string>directory-tile</string></dict>'

defaults write com.apple.dock persistent-others -array-add $tmpStr

View solution in original post

27 REPLIES 27

jamie_ivanov
New Contributor

Have you tried this?

`echo /Volumes/$user`

To give you:

<string>`echo /Volumes/$user`</string>

When you do defaults write com.apple.dock persistent-others -array-add, you are passing it a string so it will read $user as a string. You use tics to escape out of the string to execute a command which will be substituted in the string.

J.I.

jarednichols
Honored Contributor

Where, between the <string> and </string> tags? Just tried that and it gave me a dock item of $user`

ghsimon
New Contributor III

Have you tried putting $user in quotes like below?

server=dscl . -read /Users/"$user" | grep SMBHome: | cut -d '' -f 3

jarednichols
Honored Contributor

Consequently, I've also tried using ```
jamf modifyDock -file /Volumes/$user
```
But it returns an error that I need to specify an ID. Seems like a bug in the modifyDock verb as the help for it seems to indicate that you can use either -file or -id, not that it requires both.

jarednichols
Honored Contributor
Have you tried putting $user in quotes like below? server=dscl . -read /Users/"$user" | grep SMBHome: | cut -d '' -f 3

Didn't change anything. The issue seems to be how to escape the variable out so that Defaults isn't processing it literally.

jamie_ivanov
New Contributor

Have you tried:

jamf modifyDock -file /Volumes/`echo $user`

J.I.

jarednichols
Honored Contributor

jamf gives me the

You must specify a an [sic] ID. Type "jamf help modifyDock" for more info

error.

jamie_ivanov
New Contributor

or even ```
whoami
``` which will return a single string of the current username.

J.I.

rtrouton
Valued Contributor III

Can you substitute $USER in place of your existing $user? $USER should give you the current logged-in user without needing to define it otherwise.

jarednichols
Honored Contributor

Good idea, but dock item of

`whoami`

There's got to be something about Defaults that processes everything literally. It's sort of obnoxious at this point. ha

Nothing in the man page about it. Wonder if I should try plistbuddy instead.

jamie_ivanov
New Contributor

Defaults is taking a string passed to it via the shell, it doesn't see the variable at all.

J.I.

jarednichols
Honored Contributor
Can you substitute $USER in place of your existing $user? $USER should give you the current logged-in user without needing to define it otherwise.

Result: $USER dock item. It's almost comical at this point.

jarednichols
Honored Contributor

I'm going to go crowbar this in with plistbuddy. Maybe I'll have better luck.

mm2270
Legendary Contributor II

Yeah, seems to me defaults can't use a variable in that kind of command for whatever reason. I guess its just a limitation of the program.

Try PlistBuddy as you state or look at 3rd party tools. You should be able to do this with existing tools though, so its pretty strange.

jamie_ivanov
New Contributor
R0190381:~ jamieivanov$ defaults write com.apple.dock persistent-others -array-add "<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>`echo /Users/$USER`</string><key>_CFURLStringType</key><integer>0</integer></dict><key>showas</key><integer>2</integer></dict><key>tile-type</key><string>directory-tile</string></dict>"
R0190381:~ jamieivanov$ killall -9 Dock

That placed an icon on the Dock to the appropriate path. It worked just fine for me. Like I said, defaults only reads a string and using ticks (the tilde key to the left of the "1" key), not single quotes.

J.I.

henkelb
New Contributor III

This also seems to work.

tmpStr='<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>/Volumes/'$user'</string><key>_CFURLStringType</key><integer>0</integer></dict><key>showas</key><integer>2</integer></dict><key>tile-type</key><string>directory-tile</string></dict>'

defaults write com.apple.dock persistent-others -array-add $tmpStr

nessts
Valued Contributor II

when you put things inside of a 'single quote' they don't expand
what i would suggest is trying to wrap that argument in " to see if that works, but i fear the <> will get interpreted at that point.
we do something very similar in perl like this: system("defaults", "write", "$target", "persistent-apps", "-array-add", "<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>$app_path</string><key>_CFURLStringType</key><integer>0</integer></dict></dict></dict>");

nessts
Valued Contributor II

nice Bill, i was just going to test something like, that because it was gnawing at my brain how to set a tmp string in shell and pass it. i was worried that you would need single quotes in the command and then not get anything close to what you wanted to get.

mm2270
Legendary Contributor II

Looks like Jamie is spot on. Enclose it in double quotes, not single and it seems to work. In fact, I didn't need to do the echo portion. Something like the below works as is:

defaults write com.apple.dock persistent-others -array-add "<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>**/Users/$user/**</string><key>_CFURLStringType</key><integer>0</integer></dict><key>showas</key><integer>2</integer></dict><key>tile-type</key><string>directory-tile</string></dict>"

It adds my home folder to the Dock. I'm guessing the same would be true if doing /Volumes/$user/ instead.

jarednichols
Honored Contributor

Bill gets a cookie - or beverage of his choice should we run into eachother at the NUC.

Holy ba-jeebus I don't know why that was so hard.

jamie

For some reason the backticks (a.k.a grave) aren't working for my on this system (10.7.4). I get what they do but for some reason defaults for me was totally ignoring that.

jamie_ivanov
New Contributor

It wasn't so hard. Also, I'm running 10.7.4 and using bash just like everyone else. The shells are the same unless there is a problem with encoding/localizations or a keyboard malfunction. It's mere shell scripting.

Though what was difficult was the harder-than-it-needs-to-be solutions.

J.I.

tlarkin
Honored Contributor

I think Bill is on the right path here. It is like trying to pass a bash variable in a bash script, apple script, or say into an awk program. The output doesn't carry over since they are separate sets of binaries.

So, you can echo out the results, like so:

echo "a bunch of xml settings" > com.apple.somepropertlylist

or

setttings="<xml stuff that makes you go cross eyed>"

defaults write com.someplist $settings

or

echo $settings > com.someplist

I haven't tested any of these methods but I have used very similar things using bash to apple script, apple script to bash, and bash to awk.

I will try to test some of these this weekend - only if time permits.

Thanks,
Tom

jamie_ivanov
New Contributor

A little complicated much? Also, don't forget to add some newline strings to make it a little easier to read if you had to manually manipulate the plist. But don't over-engineer, that's when more problems get introduced. Keep it simple, efficient, and functional. There is no need to re-invent the wheel if it already exists.

J.I.

rockpapergoat
Contributor III

why don't you use mcx for this and skip all the fragile code?

doesn't this do about the same thing except for mounting?

com.apple.dock
MCXDockSpecialFolders-Raw

<array>
    <string>AddDockMCXOriginalNetworkHomeFolder</string>
</array>

there should be a key for com.apple.loginwindow that mounts the share automatically, too.

jamie_ivanov
New Contributor

Because the same script the original poster made could be accomplished in 2 lines as well. Mounting and creating a Dock icon.

J.I.

tlarkin
Honored Contributor

My first thought was MCX as well, but I didn't have time to test it out. I do vaguely remember something in WGM that allowed you to do this, I think. Been a while since I had to use OD to Manage Macs so I am starting to forget some of it.

-Tom

jarednichols
Honored Contributor

I originally tried MCX to plant the volume there, but due to every login essentially being an offline login with FV2, it didn't quite work the way I expected and you'd get an error that the system couldn't connect to the server. The script was really just an easy way for techs to setup a "drive map." More a stop-gap solution than anything else.