Management tools may run your scripts through bash
or sh
, resulting in errors when using features of zsh that are not found in those other shells.
One way around this is to install your script locally to a device, and then use the management tool to execute that file. This requires you to either clean up after yourself, or maintain versioning on each device. Not ideal in many situations.
This trick should allow you to be sure your script is interpreted by the specific shell of your choosing, regardless of what shell your management tool decides to use to process your script.
Pipe a HereDoc to /bin/zsh
There appear to be a few different ways to get to this same place. This Unix Stack Exchange post covers a bunch. The method below works for me with Mosyle, and should work with other management tools. If you succeed or fail with this method using another tool, please let me know.
#!/bin/sh
{
cat <<'EOF'
#!/bin/zsh
# Paste your entire script here.
# Anything between the two lines containing EOF
# will be executed as a zsh script.
echo "I am a zsh script"
EOF
} | /bin/zsh
Using <<
creates a “heredoc” and results in everything between the string EOF
will be run as an entire script, which is sent to the cat
command which prints the text, and then the |
pushes that text into the shell of your choosing. In my example, I’m |
piping output to /bin/zsh
.
Use Single Quotes On Your Delimiter
A key detail here, is that you must use single quotes when creating the delimiter for your heredoc
. Using cat <<'EOF'
prevents variables in the “zsh block” from being expanded in the primary shell (by the management tool.) This StackOverflow post gives some additional details.
A More Robust Example
zsh
has great options for splitting a file path using substitution. This example script flexes that, even if executed using /bin/sh
or /bin/bash
.
#!/bin/sh
#set -x
#####################
# Put nothing here #
#####################
{
cat <<'EOF'
#!/bin/zsh
#############################################
# Put all zsh commands between here and EOF #
#############################################
exampleFilePath="/path/to/filename.txt"
echo "This is exampleFilePath: $exampleFilePath"
echo "Length of File Path: ${#exampleFilePath}"
echo "Length of File Name: ${#exampleFilePath:t}"
echo "File Name: ${exampleFilePath:t}"
echo "File Extension: ${exampleFilePath:e}"
echo "File Name No Extension: ${exampleFilePath:r:t}"
#################################
# Nothing below this point #
#################################
EOF
} | /bin/zsh
Even if executed using /bin/sh
or /bin/bash
this example script will utilize zsh specific features for variable substitution.
% /bin/sh zshExample.sh
This is exampleFilePath: /path/to/filename.txt
Length of File Path: 21
Length of File Name: 12
File Name: filename.txt
File Extension: txt
File Name No Extension: filename
Limitations
There are definite limitations to this. For example, I was unable to determine a way to pass arguments to the zsh script block. An alternate method for that could be to write the heredoc
out to a temp file and then use chmod +x
and then execute the script using /bin/zsh /var/tmp/tempScript.sh --arguments
.
There are probably numerous other limitations and gotchas I haven’t considered here, but this trick has worked for me as long as all of the logic and variables are configured within the “zsh block.”
Mosyle Custom Commands
Mosyle CDN variables and other Custom Command features can be used within the “zsh block” without any modifications to the example script.
#!/bin/sh
#set -x
#####################
# Put nothing here #
#####################
{
cat <<'EOF'
#!/bin/zsh
#############################################
# Put all zsh commands between here and EOF #
#############################################
echo "Mosyle Variables: %ProductName% %CompanyName% %SerialNumber%
#################################
# Nothing below this point #
#################################
EOF
} | /bin/zsh
3 responses to “Running zsh Scripts Through Management Tools”
I’m sorry I don’t understand why you wrote this post.
Can’t you just begin with the proper shebang? i.e. #!/bin/zsh
chris
LikeLike
Not all management tools respect that, it depends how the agent processes it.
LikeLike
In Mosyle specifically, this command: “`#!/bin/zsh
exampleFilePath=”/path/to/filename.txt”
echo “This is exampleFilePath: $exampleFilePath”
echo “Length of File Path: ${#exampleFilePath}”
echo “Length of File Name: ${#exampleFilePath:t}”
echo “File Name: ${exampleFilePath:t}”
echo “File Extension: ${exampleFilePath:e}”
echo “File Name No Extension: ${exampleFilePath:r:t}”“`
Results in this return:
This is exampleFilePath: /path/to/filename.txt
Length of File Path: 21
/bin/bash: line 6: Length of File Name: ${#exampleFilePath:t}: bad substitution
File Name: /path/to/filename.txt
File Extension: /path/to/filename.txt
File Name No Extension:
The documentation around how this works is lacking quite a bit, but based on that error I can see that Mosyle always chooses to use bash regardless of the shebang you may enter there. This is not the only management tool in which I’ve observed this quirk.
LikeLike