I am using osascript in Bash to display a message in Notification Center (Mac OS X) via Apple Script. I am trying to pass a text variable from Bash to the script. For a variable without spaces, this works just fine, but not for one with spaces:
Defining
var1="Hello"
var2="Hello World"
and using
osascript -e 'display notification "'$var1'"'
works, but using
osascript -e 'display notification "'$var2'"'
yields
syntax error: Expected string but found end of script.
What do I need to change (I am new to this)? Thanks!
You could try to use instead :
osascript -e "display notification \"$var2\""
Or :
osascript -e 'display notification "'"$var2"'"'
This fixes the problem of manipulation of variables that contains spaces in bash. However, this solution doesn't protect against injections of osascript code. So it would be better to choose one of Charles Duffy's solutions or to use bash parameter expansion :
# if you prefer escape the doubles quotes
osascript -e "display notification \"${var2//\"/\\\"}\""
# or
osascript -e 'display notification "'"${var2//\"/\\\"}"'"'
# if you prefer to remove the doubles quotes
osascript -e "display notification \"${var2//\"/}\""
# or
osascript -e 'display notification "'"${var2//\"/}"'"'
Thank to mklement0 for this very useful suggestion !
This version is completely safe against injection attacks, unlike variants trying to use string concatenation.
osascript \
-e "on run(argv)" \
-e "return display notification item 1 of argv" \
-e "end" \
-- "$var2"
...or, if one preferred to pass code in on stdin rather than argv:
osascript -- - "$var2" <<'EOF'
on run(argv)
return display notification item 1 of argv
end
EOF
Related
So I'm trying to run multiple Applescript commands from the command line in one go. However, no matter how I try it, it won't work:
$ osascript -e "set x to 0; display dialog x"
$ osascript -e "set x to 0 \n display dialog x"
$ osascript -e "set x to 0 then display dialog x"
Is there a way to do this without saving to file?
This works for me:
osascript -e "set x to 0" -e "display dialog x"
Have a look at the -e option in the manual page for osascript in Terminal: man osascript
−e statement
Enter one line of a script. If −e is given, osascript will not look for a filename in the argument list. Multiple −e options may be given to build up a multi-line script. Because most scripts use char- acters that are special to many shell programs (for example, AppleScript uses single and double quote marks, “(”, “)”, and “∗”), the statement will have to be correctly quoted and escaped to get it past the shell intact.
You can also do e.g.:
osascript <<END
set x to 0
display dialog x
END
Or:
osascript -e '
set x to 0
display dialog x'
Your second attempt was very close to proper:
osascript -e "set x to 0
PRESS ENTER
display dialog x"
PRESS ENTER
I am trying to add the currently playing song if there is one to my ZSH prompt. I'm using a JXA command osascript -l JavaScript -e "Application('Music').currentTrack.name()". I am trying to assign it to a variable. and then echo that command.
precmd() {
SONG=$( echo -e osascript -l JavaScript -e "Application('Music').currentTrack.name()" )
LEFT=echo $SONG
RIGHT="$(dracula_time_segment) $(battery_pct_prompt)"
RIGHTWIDTH=$(($COLUMNS-${#LEFT}))
}
I've tried a number of variations eg: echo inside and outside the expression and various flags.
You don't need echo at all, as demonstrated by your later command substitutions when setting RIGHT; command substitution just takes a command and executes it.
SONG=$(osascript -l JavaScript -e "Application('Music').currentTrack.name()")
LEFT="$SONG"
You could combine the previous two commands; SONG isn't needed.
LEFT=$(osascript ...)
Is it possible to type / paste something to the console without executing the command? Something that would emulate the normal Ctrl / Cmd + c, Ctrl / Cmd + v where the text is put on the current line without being executed so the user can continue typing, deleting, etc.
For example I have the following simple script:
#!/bin/bash
echo -n "foo" | pbcopy
pbpaste
Now when I run this, it just echoes foo% and goes on the next line.
Also, even if this would work, I would prefer a solution that works on both mac os and ubuntu (as far as I know pbcopy does not come pre-installed on all linux distros).
EDIT
Edit to explain the scenario better. Imagine the script above is called foo.sh. You run the script ./foo.sh and when it finishes you have a new prompt with only the text "foo"|. The | represents the cursor.
So... You already know about pbcopy and pbpaste in macOS. You probably want to know about xclip, which is a similar interface to X selections ("the clipboard") from the command line.
Each system (Aqua and X) handles clipboard data structures differently, and I'm not aware of any single tool which will function this way on both platforms. That said, you can perhaps write scripts that are portable between both systems:
#!/usr/bin/env bash
if type xclip >/dev/null; then
clip_copy="xclip"
clip_paste="xclip -o"
elif type pbcopy >/dev/null ; then
clip_copy="pbcopy"
clip_paste="pbpaste"
else
echo "ERROR: no clipboard functions. Where am I?" >&2
exit 1
fi
Also note that pbcopy/pbpaste support different data types, whereas xclip just deals with text.
That said, both of these functions deal with stdin and stdout. If you want to actually simulate keypresses (as your edit appears to imply), you need another tool.
In the Mac world, a number of options exist. Cliclick works well for me. This tool has full mouse support, but also has an option t:, which will simulate keyboard input. It seems reasonable that one might cliclick t:"$(pbpaste)", though I've never tried it.
You can also use AppleScript to print arbitrary text:
$ osascript -e 'tell application "System Events" to keystroke "Hello world."'
In X, xdotool seems to work.
$ xdotool type "Hello world."
To make a script which might run in both macOS and X environments, you could key on the output of uname -s:
#!/usr/bin/env bash
case "$(uname -s)" in
Darwin)
clip_copy="pbcopy" # note: $clip_copy isn't used in this script.
clip_paste="pbpaste"
type_cmd="osascript -e 'tell application \"System Events\" to keystroke \"%s\"'"
;;
*)
clip_copy="xclip"
clip_paste="xclip -o"
type_cmd='xdotool type "%s"'
;;
esac
text="$($clip_paste)"
printf "$type_cmd" "${text//[!A-Za-z0-9. ]/}" | sh
Note: untested. YMMV. May contain nuts.
Based on a gist in the comments I found a thread on stack exchange that helped me answer my own question: https://unix.stackexchange.com/a/213821.
I am only interested in supporting sh, bash, zsh so the following solution works fine:
#!/bin/bash
if [ "$(echo $ZSH_VERSION)" ]; then
print -z $#
else
# assume bash or sh
bind '"\e[0n": "'"$*"'"'; printf '\e[5n'
fi
Call with source inject.sh echo foo
I use a Mac (osx sierra) and I have been learning how to use the bash. I am trying new stuff as I go along to grasp some concepts. This time I was experimenting with functions and aliases. But then I couldn't wrap my head around this problem:
I first echoed this function into the .profile to insert new aliases and functions easily into the .profile file.
function editprofile(){
echo "$#" >> ~/.profile
}
This function worked very well for some alias insertions. But after I tried to insert an alias for the script (the script below) that makes my mac sleep, I realized that the function creates some diffuculties with the cascading single and double quotes. I believe this will be the case for most scripts that uses lots of layers of single and double quotes.
osascript -e 'tell application "Finder" to sleep' && exit
The code below is me trying to use my function to insert the code above as an alias into the .profile.
editprofile 'alias _sleep=' "'" 'osascript -e' "'" 'tell application "Finder" to sleep' "'" '&& exit' "'"
The problem is that when the second script is echoed into the .profile file, I should still keep some escape characters, otherwise the code is interpretted by the bash incorrectly. I think this would also be the case with may other scripts that have this many layers of quotes, so I though I should ask if there is any way around.
P.S.
On a related note, it seems like when I type this:
function editprofile(){echo "$#" >> ~/.profile}
instead of this:
function editprofile(){
echo "$#" >> ~/.profile
}
into the .profile file, the script doesn't work. Is it because of the line breaks?
Assuming that you have a function, as opposed to an alias:
_sleep() { osascript -e 'tell application "Finder" to sleep' && exit; }
...you can emit its text with declare -f. Thus:
declare -f _sleep >>~/.profile
...or, to use your existing editprofile function:
editprofile "$(declare -f _sleep)"
The easiest approach is just that: Define the function in your local shell, then have the shell itself do the work of emitting it -- and quote that emitted content so it doesn't get field-split into individual arguments (and then have those arguments individually evaluated as globs).
If you don't want to go that route, there are approaches available; they're just varying degrees of unpleasant.
printf -v cmd_var '%q ' osascript -e 'tell application "Finder" to sleep'
...will put correctly-quoted contents into "$cmd_var". You could then:
printf -v sleep_def '_sleep() { %s && exit; }' "$cmd_var"
...which will give you a function declaration in sleep_dev that can be evaled to execute it locally, or appended to your .profile, &c.
editprofile "$sleep_def"
...will behave appropriately in that context.
I'm reading the scripts from here and trying to understand what's going on. This function performs changing the directory of a Finder window:
function ee {
osascript -e 'set cwd to do shell script "pwd"'\
-e 'tell application "Finder"'\
-e "if (${1-1} <= (count Finder windows)) then"\
-e "set the target of window ${1-1} to (POSIX file cwd) as string"\
-e 'else' -e "open (POSIX file cwd) as string"\
-e 'end if' -e 'end tell';\
};\
I'm assuming the $ is interpreted by bash, since it's inside double-quotes. I haven't been able to find what could {1-1} mean. I've played with the expression in separate test scripts but couldn't find a difference from plain $1. Any ideas?
This means that if argument 1 (${1}) is not set, it will be set to 1.
See parameter substitution here.
${parameter-default}, ${parameter:-default}
If parameter not set, use default.