Skip to content

Commit 84e0e81

Browse files
authored
Merge pull request #24 from JuliaWeb/run-string
Add support for passing strings to Base.run()
2 parents d1da2de + 9d59553 commit 84e0e81

File tree

3 files changed

+40
-10
lines changed

3 files changed

+40
-10
lines changed

docs/src/changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Changelog](https://keepachangelog.com).
1313

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

1719
### Fixed
1820

src/channel.jl

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ strings using e.g. `String(copy(process.out))`.
461461
out::Vector{UInt8} = Vector{UInt8}()
462462
err::Vector{UInt8} = Vector{UInt8}()
463463

464-
cmd::Union{Cmd, Nothing} = nothing
464+
cmd::Union{Cmd, String, Nothing} = nothing
465465
exitcode::Int = typemin(Int)
466466

467467
_sshchan::Union{SshChannel, Nothing} = nothing
@@ -494,7 +494,7 @@ function Base.wait(process::SshProcess)
494494
try
495495
wait(process._task)
496496
catch ex
497-
if !process.cmd.ignorestatus
497+
if process.cmd isa Cmd && !process.cmd.ignorestatus
498498
rethrow()
499499
end
500500
end
@@ -513,7 +513,8 @@ end
513513
function _exec_command(process::SshProcess)
514514
sshchan = process._sshchan
515515
session = sshchan.session
516-
cmd_str = Base.shell_escape(process.cmd)
516+
is_cmd = process.cmd isa Cmd
517+
cmd_str = is_cmd ? Base.shell_escape(process.cmd) : process.cmd
517518

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

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

570-
if !process.cmd.ignorestatus && process.exitcode != 0
571+
if (!is_cmd || !process.cmd.ignorestatus) && process.exitcode != 0
571572
throw(SshProcessFailedException(process))
572573
end
573574
end
@@ -580,6 +581,9 @@ supported compared to `run()`:
580581
- Pipelined commands (use a regular pipe like `foo | bar` instead).
581582
- Setting the directory to execute the command in.
582583
584+
An easy way of getting around these restrictions is to pass the command as a
585+
`String` instead of `Cmd`.
586+
583587
!!! note
584588
Setting environment variables is supported, but will fail if the server
585589
forbids setting them.
@@ -615,15 +619,23 @@ julia> ssh.Demo.DemoServer(2222; password="foo") do
615619
println()
616620
@info "2"
617621
run(ignorestatus(`foo`), session)
622+
623+
println()
624+
@info "3"
625+
# Pass a string to avoid hacking around Cmd syntax
626+
run("cd /tmp && pwd", session)
618627
end
619628
[ Info: 1
620629
foo
621630
622631
[ Info: 2
623632
sh: line 1: foo: command not found
633+
634+
[ Info: 3
635+
/tmp
624636
```
625637
"""
626-
function Base.run(cmd::Cmd, session::Session;
638+
function Base.run(cmd::Union{Cmd, String}, session::Session;
627639
wait::Bool=true, verbose::Bool=false,
628640
combine_outputs::Bool=true, print_out::Bool=true)
629641
process = SshProcess(; cmd, _verbose=verbose)
@@ -658,7 +670,7 @@ $(TYPEDSIGNATURES)
658670
659671
Read the output from the command in bytes.
660672
"""
661-
function Base.read(cmd::Cmd, session::Session)
673+
function Base.read(cmd::Union{Cmd, String}, session::Session)
662674
process = run(cmd, session; print_out=false)
663675
return process.out
664676
end
@@ -681,21 +693,21 @@ julia> ssh.Demo.DemoServer(2222; password="foo") do
681693
read(`echo foo`, session, String) = "foo\\n"
682694
```
683695
"""
684-
Base.read(cmd::Cmd, session::Session, ::Type{String}) = String(read(cmd, session))
696+
Base.read(cmd::Union{Cmd, String}, session::Session, ::Type{String}) = String(read(cmd, session))
685697

686698
"""
687699
$(TYPEDSIGNATURES)
688700
689701
`readchomp()` for remote commands.
690702
"""
691-
Base.readchomp(cmd::Cmd, session::Session) = chomp(read(cmd, session, String))
703+
Base.readchomp(cmd::Union{Cmd, String}, session::Session) = chomp(read(cmd, session, String))
692704

693705
"""
694706
$(TYPEDSIGNATURES)
695707
696708
Check the command succeeded.
697709
"""
698-
Base.success(cmd::Cmd, session::Session) = success(run(cmd, session; print_out=false))
710+
Base.success(cmd::Union{Cmd, String}, session::Session) = success(run(cmd, session; print_out=false))
699711

700712
## Direct port forwarding
701713

test/LibSSHTests.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,22 @@ end
561561
# Test setting environment variables
562562
cmd = setenv(`echo \$foo`, "foo" => "bar")
563563
@test readchomp(cmd, session) == "bar"
564+
565+
# Test command failure. We redirect error output to devnull to hide
566+
# the errors displayed by the errormonitor() task.
567+
try
568+
redirect_stderr(devnull) do
569+
run(`foo`, session)
570+
end
571+
catch ex
572+
@test ex isa TaskFailedException
573+
@test current_exceptions(ex.task)[1][1] isa ssh.SshProcessFailedException
574+
end
575+
576+
# Test passing a String instead of a Cmd
577+
mktempdir() do tmpdir
578+
@test readchomp("cd $(tmpdir) && pwd", session) == tmpdir
579+
end
564580
end
565581
end
566582

0 commit comments

Comments
 (0)