Posted on 09-30-2015 09:16 AM
Guys how do I remotely mount network shares that contain people's usernames but do it in a bash script i.e. matches the following variable:
$ echo $USER
I have tried the following:
$ myusername=$USER
$ open 'cifs://Some_Remote_Network_Share/$myusername$'
The above does not pick up the myusername variable and nothing else that I have tried does either. Sorry I know it's a stupid question but there must be a way to pass in the $USER variable !?!
Also, I think I can put the script into Casper Self Service but I was also thinking of making the bash script into an app which can be put on the dock, seems Automator can do that:
http://stackoverflow.com/questions/281372/executing-shell-scripts-from-the-os-x-dock
For those that have converted Bash scripts into "dockable" applications is Automator the best way to go?
Thanks again!
Posted on 09-30-2015 09:38 AM
Try enclosing the variable containing the username in curly brackets { }
like this:
open 'cifs://Some_Remote_Network_Share/${myusername}$'
Its possible the variable isn't being recognized because its being placed adjacent to other characters in the command line. Placing curly brackets around it prevents the shell from combining it with other characters when its read, which makes it an invalid reference.
I also don't think you need to escape the $ sign, but I'm not certain on that. Try it both ways I suppose, if the former doesn't work.
As for using Automator to make double clickable script apps, its an option, but my preference is Platypus.
Lastly, I would search around on here, because there are better approaches to what you're trying to do. This is a fairly common need and others already have scripts and process that do this you can pick up ideas from or just use as is.
Posted on 09-30-2015 09:45 AM
I have something like this available in Self Service for a client mounting DFS. It seems to work well.
#!/bin/bash
su - `ls -l /dev/console | awk '{print $3}'` -c
osascript <<EOD
set short_name to system attribute "USER"
tell application "Finder"
mount volume "cifs://Some_Remote_Netword_Share/" & short_name & ""
end tell
EOD
Posted on 09-30-2015 11:34 AM
The single quote marks would have an issue as they can't expand the variables. For that part you could try double quotes instead.
If you're not sure what its doing, try adding a echo $VARIABLENAME
after each line or variable to get some logging and info back. The echos will show up in the policy log in Casper.
I normally use AppleScript to make clickable applications using the do shell script "/path/to/shell/scrpt"
command.
Last thought, it might be worth putting the script into Casper, creating a policy with a custom trigger and then having the client call the policy with jamf policy -event trigger_name
. That way you can keep editing the script in the JSS.
Posted on 09-30-2015 02:30 PM
You can also use the JAMF binary to mount shares.
Posted on 09-30-2015 04:31 PM
Thanks everyone especially @mm2270 and @davidacland Unfortunately Platypus doesn't accept user input and I can't seem to be able to get Automator to accept user input but I think it should be handle the script below (any further ideas for getting it to be a dockable app and accepting user input several times?). Basically, Automator somehow has to be able to handle user inputs of y, y, y, and y as the mount points get added in. Thanks!
myusername=$USER
echo
echo "Hello! Which of the following network shares would you like to mount today:"
echo
read -p "1. Public Software <y/N>" public_software
if [[ $public_software == "y" || $public_software == "Y" || $public_software == "yes" || $public_software == "Yes" || $public_software == "YES" ]]
then
open 'cifs://nas-xxx/PublicSoftware'
fi
echo
read -p "2. Network Drive <y/N>" network_drive
if [[ $network_drive == "y" || $network_drive == "Y" || $network_drive == "yes" || $network_drive == "Yes" || $network_drive == "YES" ]]
then
open 'cifs://xxxxxxxxxxxxx002/networkdrive'
fi
echo
read -p "3. Personal Strategies <y/N>" personal_strategies
if [[ $personal_strategies == "y" || $personal_strategies == "Y" || $personal_strategies == "yes" || $personal_strategies == "Yes" || $personal_strategies == "YES" ]]
then
open 'cifs://xxxxxxxxxxx001/PersonalStrategies$'
fi
echo
read -p "4. Home Drive Folder (for your personal storage) <y/N>" home_folder
if [[ $home_folder == "y" || $home_folder == "Y" || $home_folder == "yes" || $home_folder == "Yes" || $home_folder == "YES" ]]
then
open "cifs://xxxxxxxxx001/${myusername}$"
fi
echo
Posted on 09-30-2015 07:42 PM
@Hafiz You never mentioned anything about user input in your OP. Diff story then. Though its possible to use Platypus with other tools bundled in (like cocoaDialog) to make double clickable apps that can ask for input, if your need here is simple enough, stick with straight Applescript if you can and make it into an .app bundle.
If you aren't able to flip this all into an Applescript, as I mentioned, you can make apps with Platypus that can use cocoaDialog as the GUI element, and Platypus is just the wrapper that makes it all into an .app someone can double click.
If you want to go that route, I can provide pointers. I've built several apps using this combination that work quite well.
Edit: BTW, I just looked at your script posted above, and there's no way you can make that kind of script work to ask for input in a GUI fashion. The 'read' command is intended to be used in a shell environment. Automator or other tools are not going to be able to use that to prompt the user for any input unless you are providing some kind of console for them to type into, which is both difficult and ugly.
Posted on 10-01-2015 01:35 AM
@mm2270 Drat! Thanks for letting me know the futility of making this into a GUI script because of the read command. Yes, okay some pointers for making Platypus with cocoaDialog into a double clickable app with user input would be useful and fun for the future (probably useful for other people as well).
Posted on 10-01-2015 08:57 AM
@Hafiz Would something that pops up a dialog like the following work for your users? So they can just check the items they want mounted?
Posted on 10-01-2015 09:47 AM
I've been trying to think of the other app I used all day and just remembered! Pashua lets you easily make scripts that have a GUI interface.
Looks like @mm2270 has already offered a pretty neat solution but I just wanted to get pashua out of my head!
Posted on 10-02-2015 07:12 AM
@Hafiz Are you still needing help with this? Any interest in what I posted above? If so, just post back and I can show you the details. I'm not going to bother writing up a long post if this isn't something you're looking for.
Posted on 10-03-2015 03:01 AM
@mm2270 Yes, I am still interested in creating a dockable app for all users that will give them network shares that they can choose to mount and a short description of the share itself. Done graphically and deployed out to all the MacBook users it should prove to be quite useful.
Posted on 10-05-2015 09:23 AM
Hi @Hafiz Ok, I have some time now to write this up. Here is a little tutorial on how to build an app with Platypus that uses cocoaDialog for parts or all of the UI.
First thing I wanted to post was the script I generated that will do the actual work. This is what created the dialog image I show above.
#!/bin/bash
####################### Variables #######################
## Get the logged in user's name
loggedInUser=$(stat -f%Su /dev/console)
## Get the script's working directory
workingDir="$( pwd )"
## Generate the path to the app's embedded cocoaDialog
cdPath="${workingDir}/cocoaDialog.app/Contents/MacOS/cocoaDialog"
## Path to the sharepoint icon, used in the dialog
networkIcon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericFileServerIcon.icns"
##################### End variables #####################
##################### Static Arrays #####################
## Three arrays:
## One for the human readable name for the dialog
## One for the mount points
## One for the server names
ShareNames=("Public Software" "Network Drive" "Personal Strategies" "Home Drive Folder")
Shares=("PublicSoftware" "networkdrive" "PersonalStrategies" "${loggedInUser}")
## Edit this list to match the actual server names
Servers=("nas-xxx" "xxxxxxxxxxxxx002" "xxxxxxxxxxx001" "xxxxxxxxx001")
################### End Static Arrays ###################
## This is the function that does the actual mounts of the sharepoints selected by the user
function mountShares ()
{
## Loop over the SelectedShares array,
## mounting each share using its corresponding "Servers" path and the share name
x=0
while read shareName; do
open "cifs://${ServerPaths[$x]}/${shareName}$"
let x=$((x+1))
done < <(printf '%s
' "${SelectedShares[@]}")
exit 0
}
#################### Start of script ####################
## Create a cocoaDialog checkbox dialog asking the user to select from a list of shares to mount
RequestDialog=$("$cdPath" checkbox
--title ""
--items "${ShareNames[@]}"
--label "Hello! Which of the following network shares would you like to mount today:"
--button1 "Connect"
--button2 " Cancel "
--cancel "button2"
--width 400
--icon-file "$networkIcon"
--value-required
--empty-text "Please check at least one item before clicking Connect. Or click Cancel to exit."
--quiet)
## Since we are using the 'value-required' flag, the user must check at least one item, or cancel the dialog.
## We are also using the 'quiet' flag which suppresses the button clicked. Therefore, if the command returned any values,
## the user must have checked some items and clicked "Connect". If so, proceed with reading the checked items values.
if [ ! -z "$RequestDialog" ]; then
## Create array of checked items to loop over
while read box; do
CheckedItems+=($box)
done < <(printf '%s
' "$RequestDialog")
## Loop over the CheckedItems array, creating 2 new arrays,
## for SelectedShares (share names), and ServerPath
x=0
while read item; do
if [ "$item" == "1" ]; then
SelectedShares+=("${Shares[$x]}")
ServerPaths+=("${Servers[$x]}")
fi
let x=$((x+1))
done < <(printf '%s
' "${CheckedItems[@]}")
## If we got this far, move on to the mountShares function to actually mount them.
mountShares
else
## if we got an empty result back from the dialog, the user canceled, so exit
echo "User canceled. Exiting..."
exit 1
fi
Now, there are a few things (some semi advanced) going on in the script, so I should explain it a bit. I put comments into the script, but just so its clear what's happening...
In the Variables section, I'm setting a couple of items.loggedInUser
- This just gets the logged in user name. About 10 different variations or ways to do this. This is just one. It pulls the short name of the logged in accountworkingDir
- This is an important one. So, Platypus always runs the script from the .apps "Resources" directory. We can take advantage of this fact since any bundles tools, like cocoaDialog, also go into the Resources
directory. So this is just getting the script's working directory, so it knows where to find cocoaDialog. This allows the final .app to run from literally any location. The Desktop, the Applications folder, off a thumb drive, wherever.cdPath
- Here I'm using the workingDir
path we got above to tell the script where the full path to the cocoaDialog binary is. Again, this allows it to run self contained from any location.networkIcon
- Nothing special here. Just a hardcoded path to the GenericFileServerIcon .icns file we'll use in the dialog. Note that its also possible to copy this icon or even a custom one into the Resources folder and use the same trick as with cdPath to generate a path to the icon. I've done that before to ensure the icon will always appear correctly.
In the Static Arrays section, I'm setting 3 static bash arrays, which are special kinds of lists, in case you haven't used those before.ShareNames
- These are the share names we'll use in the dialog. These can be labeled whatever you want, including having some descriptions after them if need be.Shares
- These are the actual share names as they exist on the server, so these need to be exact as they'd be called in the open command later.Servers
- Like above, these are the server names the shares come from.
The only important thing about these arrays is that a) They need to be the same qty across them all, so in this case, 4, and b) They have to be in the same order, so "Public Software" matches up with "PublicSoftware" and "nas-xxx" as the first item respectively. If they are out of order or not the same quantity, the script won't work right.
The next item is a function called mountShares. I'll come back to that since it may not make complete sense what's happening until you go thru the rest of the script.
If you go down to Start of Script, I have a variable I'm creating using a cocoaDialog call. The dialog uses the checkbox style. I'm passing the ShareNames
array to the --items
flag, which sets each share name as its own checkbox. There's some dialog text, 2 buttons, one designated as the 'cancel' button, a width setting and the network icon.
The last 3 items in the cocoaDialog call are important to understand. The --value-required
flag tells cocoaDialog not to exit with the default button ("Connect" in this case), when asking for user input (checkboxes, radio buttons, text fields, etc) The user must do something or select something before they can click the "Connect" button. If they don't, they get the text that shows up after the --empty-text
flag.
The --cancel
flag we set earlier allows them to click Cancel to exit the script if they want.
Last is the --quiet
flag. This suppresses the button clicked callback. Normally we'd see a "1" or "2" depending on the button clicked. Since we're forcing either a selection or a cancel from the user, we only need to check to see if the variable got filled with some result. If it comes back null, then they canceled.
That's what we're doing next in the script. Theif [ ! -z "$RequestDialog" ]; then
basically says, if RequestDialog isn't empty, then the user checked at least one box, so let's continue!
The next item (while read box; do
) is a loop that converts the string of 0s and 1 from the checkboxes returned into an array I can process later.
The while read item; do
loop now takes the array I just built and checks each one to see if we see a "1" which means checked. For each "1" we build 2 more arrays, for the SelectedShares and ServerPaths This is using the array index or indices, basically the $x
is the current index, starting from 0 and moves up 1 with each loop, so it knows which strings to pass back to the arrays as it builds them.
Finally, now that we have a few arrays we can use, we move on to the mountShares
function from above. Here, I'm just looping over the SelectedShares array and for each share, setting the corresponding ServerPath and using the open smb:// something something
command to open each one for the user. Then it exits.
What's not happening in this script at all, but should, is checking for a network connection. Since your users can run this from their Mac and not just from something like Self Service, it would be a good idea before throwing up a dialog, to check for the network so it can mount the shares. Maybe a simple ping to a primary domain controller or some other internal server that should always be available for example. I'll leave that up to you to determine. The script as is, won't account for situations where it can't reach the network, so it'll just exit with nothing mounting if it can't reach any of them.
So hopefully that all makes sense what's happening in the script and will give you some ideas.
Now, for building this into an actual app,
Oh, there is a bug that prevents the icon you drag into that image well from being applied when you build the app using the GUI. So don't bother to put a custom icon there. You can copy/paste a custom icon onto the final app using the Finder and it sticks.
Give all that a try and let me know if you run into any trouble.
BTW, if you want to first test the script, you can create a new cdPath
variable with a hardcoded path to cocoaDialog on your Mac for testing purposes and just comment out the other one. That's generally what I do. Once I have it all working, I remove the hardcoded path and uncomment the one using $workingDir as the start of its path and build the app.
Posted on 10-09-2015 09:18 AM
@mm2270 Thank you so much! I am sure others will find this useful too. I will try this out next week and let you know how I get on. Cheers!