Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

osproc.execCmdEx now takes an optional input for stdin, env, workingDir #14211

Merged
merged 2 commits into from
May 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@
- The callback that is passed to `system.onThreadDestruction` must now be `.raises: []`.


- `osproc.execCmdEx` now takes an optional `input` for stdin.
- `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env`
parameters.

## Language changes
- In the newruntime it is now allowed to assign discriminator field without restrictions as long as case object doesn't have custom destructor. Discriminator value doesn't have to be a constant either. If you have custom destructor for case object and you do want to freely assign discriminator fields, it is recommended to refactor object into 2 objects like this:

Expand Down
43 changes: 31 additions & 12 deletions lib/pure/osproc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1438,12 +1438,16 @@ elif not defined(useNimRtl):


proc execCmdEx*(command: string, options: set[ProcessOption] = {
poStdErrToStdOut, poUsePath}): tuple[
poStdErrToStdOut, poUsePath}, env: StringTableRef = nil,
workingDir = "", input = ""): tuple[
output: TaintedString,
exitCode: int] {.tags:
[ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
## A convenience proc that runs the `command`, grabs all its output and
## exit code and returns both.
## A convenience proc that runs the `command`, and returns its `output` and
## `exitCode`. `env` and `workingDir` params behave as for `startProcess`.
## If `input.len > 0`, it is passed as stdin.
## Note: this could block if `input.len` is greater than your OS's maximum
## pipe buffer size.
##
## See also:
## * `execCmd proc <#execCmd,string>`_
Expand All @@ -1452,17 +1456,32 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
## * `execProcess proc
## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
##
## Example:
##
## .. code-block:: Nim
## let (outp, errC) = execCmdEx("nim c -r mytestfile.nim")

var p = startProcess(command, options = options + {poEvalCommand})
runnableExamples:
var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4")
import strutils, strtabs
stripLineEnd(result[0]) ## portable way to remove trailing newline, if any
doAssert result == ("12", 0)
doAssert execCmdEx("ls --nonexistant").exitCode != 0
when defined(posix):
assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0)
assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0)

when (NimMajor, NimMinor, NimPatch) < (1, 3, 5):
doAssert input.len == 0
doAssert workingDir.len == 0
doAssert env == nil

var p = startProcess(command, options = options + {poEvalCommand},
workingDir = workingDir, env = env)
var outp = outputStream(p)

# There is no way to provide input for the child process
# anymore. Closing it will create EOF on stdin instead of eternal
# blocking.
if input.len > 0:
# There is no way to provide input for the child process
# anymore. Closing it will create EOF on stdin instead of eternal
# blocking.
# Writing in chunks would require a selectors (eg kqueue/epoll) to avoid
# blocking on io.
inputStream(p).write(input)
close inputStream(p)

result = (TaintedString"", -1)
Expand Down
3 changes: 2 additions & 1 deletion lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2068,8 +2068,9 @@ const
## is the minor number of Nim's version.
## Odd for devel, even for releases.

NimPatch* {.intdefine.}: int = 3
NimPatch* {.intdefine.}: int = 5
## is the patch number of Nim's version.
## Odd for devel, even for releases.

import system/dollars
export dollars
Expand Down
15 changes: 14 additions & 1 deletion tests/stdlib/tosproc.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# test the osproc module

import stdtest/specialpaths
import "../.." / compiler/unittest_light
import "$nim" / compiler/unittest_light

when defined(case_testfile): # compiled test file for child process
from posix import exitnow
Expand Down Expand Up @@ -119,3 +119,16 @@ else:

var result = startProcessTest("nim r --hints:off -", options = {}, input = "echo 3*4")
doAssert result == ("12\n", 0)

import std/strtabs
block execProcessTest:
var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4")
stripLineEnd(result[0])
doAssert result == ("12", 0)
doAssert execCmdEx("ls --nonexistant").exitCode != 0
when false:
# bug: on windows, this raises; on posix, passes
doAssert execCmdEx("nonexistant").exitCode != 0
when defined(posix):
doAssert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0)
doAssert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0)