Interpolation issues in a backticks expression


#1

I’m trying to build this command:

 osascript -e 'tell application "Terminal" to do script "julia -i $path/file.jl"'

Where $path is a julia variable, But I can’t manage to get it to work, any idea ?

Thanks.


#2

Seems to be single quotes that are preventing variable expansion in command.
Escaping them expands $path in command

path="/tmp/path"
cmd=`osascript -e \'tell application "Terminal" to do script "julia -i $path/file.jl"\'`
julia> dump(cmd)
Cmd
  exec: Array{String}((9,))
    1: String "osascript"
    2: String "-e"
    3: String "'tell"
    4: String "application"
    5: String "Terminal"
    6: String "to"
    7: String "do"
    8: String "script"
    9: String "julia -i /tmp/path/file.jl'"
  ignorestatus: Bool false
  flags: UInt32 0
  env: Void nothing
  dir: String ""

#3

The problem here is that you have to know how escaping rules work for osascript. For example, what if the path contains a space, a quote, a dollar sign, or some other special character? Assuming it uses standard shell-like escaping, you can maybe do (I don’t really know osascript) something like:

p = sprint(escape_string, joinpath(path, "file.jl"), "\$!")
s = "tell application \"Terminal\" to do script \"julia -i \\\"$p\\\"\""
`osascript -e $s`

Note that you want to pass the argument to -e as a single string. No additional quotes are needed around the string, because the osascript command is not being executed in the shell.

Avoiding this kind of escaping issue is precisely the reason that backtick-commands in Julia avoid the shell, so you are re-opening that can of worms by passing through two layers of shell: one in the osascript command that you are trying to construct, and another in the shell command executed by the osascript command. (I may be missing another level of backslashes in the nested shell command above! You’ll need to test things out.)


#4

I just tried the code I suggested, and an extra level of backslashes is required for filenames containing $:

using Compat
sescape(s) = replace(sprint(escape_string, s, "\$!"), "\\"=>"\\\\")

p = sescape(joinpath(path, "file.jl"))
s = "tell application \"Terminal\" to do script \"julia -i \\\"$p\\\"\""
run(`osascript -e $s`)

Again, all these backslashes are essentially because you are going through three levels of escaping: Julia literal string escaping, osascript’s string escaping, and the shell escaping.

(Even this might not be enough escaping for file names containing " characters, I haven’t tried that.)


#5

That works yes, thanks!