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

* `osproc.execCmdEx` now takes an optional `input` for stdin

* execCmdEx now also takes an optional ``workingDir` and `env`
This commit is contained in:
Timothee Cour
2020-05-13 04:45:36 -07:00
committed by GitHub
parent 1648f1dd99
commit 041ee92bba
4 changed files with 50 additions and 13 deletions

View File

@@ -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:

View File

@@ -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>`_
@@ -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")
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)
var p = startProcess(command, options = options + {poEvalCommand})
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)

View File

@@ -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

View File

@@ -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
@@ -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)