Description
When interpolating in a Cmd
literal, if you try to interpolate in a Cmd
object with a non-default environment block, only the first interpolant is allowed to be inserted. Example:
julia> Cmd(`ffmpeg`; env=Dict("PATH" => "path1"))
setenv(`ffmpeg`,["PATH=path1"])
julia> ffmpeg = Cmd(`ffmpeg`; env=Dict("PATH" => "path1"))
setenv(`ffmpeg`,["PATH=path1"])
julia> shell = `/bin/bash`
`/bin/bash`
julia> run(`$(shell) -c $(ffmpeg)`)
ERROR: ArgumentError: Non-default environment behavior is only permitted for the first interpolant.
Stacktrace:
[1] arg_gen(cmd::Cmd)
@ Base ./cmd.jl:357
[2] cmd_gen(parsed::Tuple{Tuple{Cmd}, Tuple{SubString{String}}, Tuple{Cmd}})
@ Base ./cmd.jl:391
[3] top-level scope
@ REPL[11]:1
This is, I assume, because it is not entirely obvious what to do with the environment blocks of these Cmd
objects. Do we merge them? If so, what precedence do we take? Personally, I think the most natural thing to do would be to merge them in left-to-right order and leave it up to the user to ensure there are no conflicts, but the code is complicated enough that I am willing to accept that this may not be easy.
The real kicker is that despite the error message, the true requirement is that only the first word of a Cmd
may be a Cmd
interpolant with an env
block:
julia> shell = Cmd(`/bin/bash`; env=Dict("FOO" => "foofoo"))
run(`sudo $(shell) -c echo \$FOO`)
ERROR: ArgumentError: Non-default environment behavior is only permitted for the first interpolant.
Stacktrace:
[1] arg_gen(cmd::Cmd)
@ Base ./cmd.jl:357
[2] cmd_gen(parsed::Tuple{Tuple{SubString{String}}, Tuple{Cmd}, Tuple{SubString{String}}, Tuple{SubString{String}}, Tuple{SubString{String}}})
@ Base ./cmd.jl:396
[3] top-level scope
@ REPL[6]:2
This is clearly a bug, and presents a usability issue for things like the new executable wrappers that BB provides. We want to move away from a withenv()
-based system (due to thread-unsafety) and towards an addenv()
-based system, where you get passed a Cmd
block with the necessary PATH
and LD_LIBRARY_PATH
and other variables set already within it. However, if you needed to, say, call sudo
before invoking your executable, you would have to use this shameful workaround:
julia> using UserNSSandbox_jll
sandbox_cmd = sandbox()
run(Cmd(`sudo $(sandbox_cmd.exec) --verbose --rootfs /tmp/rootfs /bin/bash`; env=sandbox_cmd.env))
Not quite as nice as simply:
julia> using UserNSSandbox_jll
run(`sudo $(sandbox()) --verbose --rootfs /tmp/rootfs /bin/bash`)
In the short term, fixing the error would be good, but long-term I think it would be best if we remove this limitation altogether.