Skip to content

Errors regarding Cmd interpolation with environment blocks #39282

Open
@staticfloat

Description

@staticfloat

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.

Metadata

Metadata

Labels

cmdRelates to calling of external programs

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions