Skip to content

Commit

Permalink
Merge pull request #24 from JuliaWeb/run-string
Browse files Browse the repository at this point in the history
Add support for passing strings to Base.run()
  • Loading branch information
JamesWrigley authored Oct 17, 2024
2 parents d1da2de + 9d59553 commit 84e0e81
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 10 deletions.
2 changes: 2 additions & 0 deletions docs/src/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Changelog](https://keepachangelog.com).

- Added support for setting the file descriptor for a [`Session`](@ref) during
construction ([#21]).
- Our [`Base.run()`](@ref) methods now accept plain `String`s as well as `Cmd`s
([#24]).

### Fixed

Expand Down
32 changes: 22 additions & 10 deletions src/channel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ strings using e.g. `String(copy(process.out))`.
out::Vector{UInt8} = Vector{UInt8}()
err::Vector{UInt8} = Vector{UInt8}()

cmd::Union{Cmd, Nothing} = nothing
cmd::Union{Cmd, String, Nothing} = nothing
exitcode::Int = typemin(Int)

_sshchan::Union{SshChannel, Nothing} = nothing
Expand Down Expand Up @@ -494,7 +494,7 @@ function Base.wait(process::SshProcess)
try
wait(process._task)
catch ex
if !process.cmd.ignorestatus
if process.cmd isa Cmd && !process.cmd.ignorestatus
rethrow()
end
end
Expand All @@ -513,7 +513,8 @@ end
function _exec_command(process::SshProcess)
sshchan = process._sshchan
session = sshchan.session
cmd_str = Base.shell_escape(process.cmd)
is_cmd = process.cmd isa Cmd
cmd_str = is_cmd ? Base.shell_escape(process.cmd) : process.cmd

# Open the session channel
ret = _session_trywait(session) do
Expand All @@ -524,7 +525,7 @@ function _exec_command(process::SshProcess)
end

# Set environment variables
if !isnothing(process.cmd.env)
if is_cmd && !isnothing(process.cmd.env)
for env_var in process.cmd.env
# We explicitly convert the SubString's returned from split() to
# String's so that they're each separate and null-terminated in
Expand Down Expand Up @@ -567,7 +568,7 @@ function _exec_command(process::SshProcess)
throw(LibSSHException("Error while reading data from channel: $(ret)"))
end

if !process.cmd.ignorestatus && process.exitcode != 0
if (!is_cmd || !process.cmd.ignorestatus) && process.exitcode != 0
throw(SshProcessFailedException(process))
end
end
Expand All @@ -580,6 +581,9 @@ supported compared to `run()`:
- Pipelined commands (use a regular pipe like `foo | bar` instead).
- Setting the directory to execute the command in.
An easy way of getting around these restrictions is to pass the command as a
`String` instead of `Cmd`.
!!! note
Setting environment variables is supported, but will fail if the server
forbids setting them.
Expand Down Expand Up @@ -615,15 +619,23 @@ julia> ssh.Demo.DemoServer(2222; password="foo") do
println()
@info "2"
run(ignorestatus(`foo`), session)
println()
@info "3"
# Pass a string to avoid hacking around Cmd syntax
run("cd /tmp && pwd", session)
end
[ Info: 1
foo
[ Info: 2
sh: line 1: foo: command not found
[ Info: 3
/tmp
```
"""
function Base.run(cmd::Cmd, session::Session;
function Base.run(cmd::Union{Cmd, String}, session::Session;
wait::Bool=true, verbose::Bool=false,
combine_outputs::Bool=true, print_out::Bool=true)
process = SshProcess(; cmd, _verbose=verbose)
Expand Down Expand Up @@ -658,7 +670,7 @@ $(TYPEDSIGNATURES)
Read the output from the command in bytes.
"""
function Base.read(cmd::Cmd, session::Session)
function Base.read(cmd::Union{Cmd, String}, session::Session)
process = run(cmd, session; print_out=false)
return process.out
end
Expand All @@ -681,21 +693,21 @@ julia> ssh.Demo.DemoServer(2222; password="foo") do
read(`echo foo`, session, String) = "foo\\n"
```
"""
Base.read(cmd::Cmd, session::Session, ::Type{String}) = String(read(cmd, session))
Base.read(cmd::Union{Cmd, String}, session::Session, ::Type{String}) = String(read(cmd, session))

"""
$(TYPEDSIGNATURES)
`readchomp()` for remote commands.
"""
Base.readchomp(cmd::Cmd, session::Session) = chomp(read(cmd, session, String))
Base.readchomp(cmd::Union{Cmd, String}, session::Session) = chomp(read(cmd, session, String))

"""
$(TYPEDSIGNATURES)
Check the command succeeded.
"""
Base.success(cmd::Cmd, session::Session) = success(run(cmd, session; print_out=false))
Base.success(cmd::Union{Cmd, String}, session::Session) = success(run(cmd, session; print_out=false))

## Direct port forwarding

Expand Down
16 changes: 16 additions & 0 deletions test/LibSSHTests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,22 @@ end
# Test setting environment variables
cmd = setenv(`echo \$foo`, "foo" => "bar")
@test readchomp(cmd, session) == "bar"

# Test command failure. We redirect error output to devnull to hide
# the errors displayed by the errormonitor() task.
try
redirect_stderr(devnull) do
run(`foo`, session)
end
catch ex
@test ex isa TaskFailedException
@test current_exceptions(ex.task)[1][1] isa ssh.SshProcessFailedException
end

# Test passing a String instead of a Cmd
mktempdir() do tmpdir
@test readchomp("cd $(tmpdir) && pwd", session) == tmpdir
end
end
end

Expand Down

0 comments on commit 84e0e81

Please sign in to comment.