1

What I am trying to do is (in Mac OS X / MacOS) create a dialog in using osascript. I'd like to add an icon to it. This part is easy and works.

However, the problem is that if the user is in dark or light mode my icon file won't look right. So, I've got two .icns files which is stored on all Macs in my environment in a specific directory (that directory is located at /Library/ctxsit/). I can call up one or the other using a variable, but when I add a function to determine which file to use, it fails.

First, the part that works is:

icon=(/Library/ctxsit/CompanyIconB.icns)
title="Company IT Notification"

echo $title

prompt=$(osascript -e "display dialog \"Click Continue to logout from current user and set this Mac back to the Welcome Screen. This will leave the current user directory and profile intact.

Please make sure your work is saved. After Continuing, the device will appear to be in a Fresh OS state.  
        \" buttons {\"Cancel\", \"Continue\"} with title \"$title\" default button 2 with icon \"$icon\" ")
if [[ $prompt == "button returned:Continue" ]] 
then
    sleep 2
    prompt=$(osascript -e "display dialog \"Are you sure? Clicking Continue will run the script and Restart this Mac. 

        \" buttons {\"Continue\", \"Cancel\"} with title \"$title\" default button 2 with icon \"$icon\" ")

fi

If the Mac is in Dark mode, then that first line of my code would have

icon=(/Library/ctxsit/CompanyIconW.icns)

All above is in the "working" version of my script. So, I have added a function to the "non-working" version.


Next, the part which does not work is:

#!/bin/bash

currentOS=$(sw_vers -productVersion)
currentUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }' )
#mode=$(sudo -u "$currentUser" defaults read -g AppleInterfaceStyle)
OScheck=$([[ (($currentOS < 10.14)) ]]; echo $?)


function setIcon()
{
    if [[ $OScheck == "1" ]]; then
        echo "Mojave or newer"
        if [[ $mode == "Dark" ]]; then
            echo "Dark mode enabled"
            icon="CompanyIconW.icns"
        else
            echo "Light mode enabled"
            icon="CompanyIconB.icns"
        fi
    else
        echo "Older than Mojave"
        icon="CompanyIconB.icns"
    fi
}
setIcon
echo $icon

title="Company IT Notification"
echo $title

prompt=$(osascript -e "display dialog \"Click Continue to logout from current user and set this Mac back to the Welcome Screen. This will leave the current user directory and profile intact.

Please make sure your work is saved. After Continuing, the device will appear to be in a Fresh OS state.  
        \" buttons {\"Cancel\", \"Continue\"} with title \"$title\" default button 2 with icon \"$icon\" ")


if [[ $prompt == "button returned:Continue" ]] 
then

    sleep 2
    prompt=$(osascript -e "display dialog \"Are you sure? Clicking Continue will run the script and Restart this Mac. 
        \" buttons {\"Continue\", \"Cancel\"} with title \"$title\" default button 2 with icon \"$icon\" ")

fi

Finally, when I run this script from terminal, I get this result:

bash-3.2$ ./Scratch2.sh
Mojave or newer
Light mode enabled
CompanyB.icns
Company IT Notification
0:393: execution error: A resource wasn’t found. (-192)
0:134: execution error: A resource wasn’t found. (-192)

The only part I can think of to address is how in my "working" version, I have an actual path-to-file whereas my "non-working" version does not include that piece. This leads to my main question:

How can I set a variable based off of the return/result/exit code/ of a function?

e.g. take whichever mode the computer is in (light, dark) and use file 1 for light and file 2 for dark.

2 Answers 2

2

Some bash's tricks

Here is other robust way for doing this...

Consider Why avoid subshells is a good practice, here a no other forks than to apple binaries.

Populating variables:

read -r currentOS < <(exec sw_vers -productVersion)
while IFS=$' :\t\r\n' read -r lhs rhs ;do
    [ "$lhs" = "Name" ] && currentUser=$rhs
done   < <(exec Scutil <<<"show State:/Users/ConsoleUser")

Comparing version number:

compareOSver() {
    local osMaj osMin osSub cStr1 cStr2
    local -i retVal
    IFS=. read -r osMaj osMin osSub _ <<<"$1"
    printf -v cStr1 %06d "$osMaj" "$osMin" "$osSub"
    IFS=. read -r osMaj osMin osSub <<<"$2"
    printf -v cStr2 %06d "$osMaj" "$osMin" "$osSub"
    retVal="10#${cStr1} > 10#${cStr2} ? 1 : 10#${cStr2} > 10#${cStr1} ? 2 : 0"
    return $retVal
}

Passing variable from function to script:

function setIcon() {
    local returnVar=${1:-iconRes}
    compareOSver "$2" "$3"
    case $? in
        1 ) printf -v $returnVar 'Newer_OS.icn' ;;
        2 ) printf -v $returnVar 'Older_OS.icn' ;;
        * ) printf -v $returnVar 'Same_OS.icn' ;;
    esac
}

So you could:

setIcon icon "$currentOS" 10.14

My first tries using osascript:

DialogStr='Click Continue to logout from current user and set this Mac back to \
the Welcome Screen. This will leave the current user directory and profile intact.

Please make sure your work is saved. After Continuing, the device will appear to \
be in a Fresh OS state.'

title="Company IT Notification"

printf -v osaCmd 'display dialog "%s" buttons {"Cancel","Continue"} \
        with title "%s" default button 2 with icon %d' \
        "${DialogStr//$'\\\\\n'}" "$title" 0

IFS=: read -r _ userAnswer < <(exec osascript -e "${osaCmd//$'\\\\\n'}")

Ok I've find here: Change icon of notification when using osascript a way of using alternate icon, correct syntax is icon alias "file:path:in:mac:format:icon.icn"

testIcon=/Volumes/Macintosh\ HD/Applications/Utilities/Terminal.app/Contents/Resources/term_icon.icns 

Then

testIcon=${testIcon#/Volumes/}
printf -v osaCmd 'display dialog "%s" buttons {"Cancel","Continue"} \
    with title "%s" default button 2 with icon alias "%s"' \
    "${DialogStr//$'\\\\\n'}" "$title" "${testIcon//\//:}"

IFS=: read -r _ userAnswer < <(exec osascript -e "${osaCmd//$'\\\\\n'}")
Sign up to request clarification or add additional context in comments.

3 Comments

Seem you are using wrong syntax for setting dialog box's icon.
Bash doesn't always reliably optimize away the fork() before exec'ing the sole external command in a command substitution, so you may be able to make this even more efficient than it already is by making it <(exec osascript ...) instead of merely <(osascript ...).
@CharlesDuffy Correct! Answer edited, thanks!
1

The way you assign the value icon is "right", but I think your condition expression is wrong:

OScheck=$([[ (($currentOS < 10.14)) ]]; echo $?)

Besides the fact that you use < instead of -lt, decimal arithmetic doesn't work in Bash, so you would have to rely on external tools like bc:

OScheck=$(echo "$currentOS < 10.14" | bc) # 1 = true, 0 = false

Update your case statement accordingly.

As a side note, icon=(/Library/ctxsit/CompanyIconB.icns) is virtually the same as icon="CompanyIconB.icns" except the first one is an explicit assignment to an array, and the second one does not include a directory path.

Also avoid "returning values" from a function by using command substitution as it is terribly inefficient. Use a temporary global variable instead.

# Don't do this!
function x { echo "This is my result."; }
my_var=$(x) 

# Do this instead.
function x { __="This is my result."; }
x
my_var=$__

Update

As mentioned in the comments, comparing version numbers as decimals is not accurate, so I suggest using a function that compares them. This needs format of input arguments checked.

function compare_versions {
    local a=${1%%.*} b=${2%%.*}
    [[ "10#${a:-0}" -gt "10#${b:-0}" ]] && return 1
    [[ "10#${a:-0}" -lt "10#${b:-0}" ]] && return 2
    a=${1:${#a} + 1} b=${2:${#b} + 1}
    [[ -z $a && -z $b ]] || compare_versions "$a" "$b"
}

17 Comments

Comparing version numbers by relational operators (< or >) is not a correct method. Assume that currentOS is 10.2. As a decimal number 10.2 is greater than 10.14, but as a version number 10.14 is greater than 10.2 .
@M.NejatAydin Have a look at my compareOSver function! There as some bashisms...
Something like that could be simpler to compare versions: [[ $(IFS=.; printf '%09d' $v1) < $(IFS=.; printf '%09d' $v2) ]], with the assumption that versions $v1 and $v2 consist of decimal digits and dots.
@M.NejatAydin In my function, I'll consider major, minor and sub-release!, All are merged in one string, using: printf -v cStr1 %06d "$osMaj" "$osMin" "$osSub". (note quotes around %06d are useless here;), but double-quottes around $osSub ensure a 0 will be used as default.
@F.Hauri M. Nejat Aydin's method can be changed to printf -v v1x '%09d' ${v1//./ }; printf -v v2x '%09d' ${v2//./ }; [[ $v1x < $v2x ]] although it gets a little bit messy there. Personally I would still use a function. Still his method is good enough for immediate tests, and it should also work on version numbers with varying number of parts.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.