From fb64b6017b1f9ebd30e2ffbad9280eb210318b64 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sat, 2 May 2020 11:05:09 +0100 Subject: [PATCH] Add shell.execute This functions the same as shell.run, but does not tokenise the arguments. This allows us to pass command line arguments through to another program without having to re-quote them. Closes #417 --- .../lua/rom/programs/monitor.lua | 2 +- .../computercraft/lua/rom/programs/shell.lua | 26 ++++++++++++++++--- .../resources/test-rom/data/dump-args.lua | 1 + .../test-rom/spec/programs/shell_spec.lua | 24 ++++++++++------- 4 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 src/test/resources/test-rom/data/dump-args.lua diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua b/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua index e6daed9e8..01c9d7949 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua @@ -28,7 +28,7 @@ local monitor = peripheral.wrap(sName) local previousTerm = term.redirect(monitor) local co = coroutine.create(function() - shell.run(sProgram, table.unpack(tArgs, 3)) + (shell.execute or shell.run)(sProgram, table.unpack(tArgs, 3)) end) local function resume(...) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua b/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua index 474d3ecfd..843e28e71 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua @@ -130,8 +130,25 @@ else bgColour = colours.black end -local function run(_sCommand, ...) - local sPath = shell.resolveProgram(_sCommand) +--- Run a program with the supplied arguments. +-- +-- Unlike @{shell.run}, each argument is passed to the program verbatim. While +-- `shell.run("echo", "b c")` runs `echo` with `b` and `c`, +-- `shell.execute("echo", "b c")` runs `echo` with a single argument `b c`. +-- +-- @tparam string command The program to execute. +-- @tparam string ... Arguments to this program. +-- @treturn boolean Whether the program exited successfully. +-- @usage Run `paint my-image` from within your program: +-- +-- shell.execute("paint", "my-image") +function shell.execute(command, ...) + expect(1, command, "string") + for i = 1, select('#', ...) do + expect(i + 1, select(i, ...), "string") + end + + local sPath = shell.resolveProgram(command) if sPath ~= nil then tProgramStack[#tProgramStack + 1] = sPath if multishell then @@ -144,7 +161,7 @@ local function run(_sCommand, ...) local sDir = fs.getDir(sPath) local env = createShellEnv(sDir) - env.arg = { [0] = _sCommand, ... } + env.arg = { [0] = command, ... } local result = os.run(env, sPath, ...) tProgramStack[#tProgramStack] = nil @@ -196,11 +213,12 @@ end -- @usage Run `paint my-image` from within your program: -- -- shell.run("paint", "my-image") +-- @see shell.execute Run a program directly without parsing the arguments. function shell.run(...) local tWords = tokenise(...) local sCommand = tWords[1] if sCommand then - return run(sCommand, table.unpack(tWords, 2)) + return shell.execute(sCommand, table.unpack(tWords, 2)) end return false end diff --git a/src/test/resources/test-rom/data/dump-args.lua b/src/test/resources/test-rom/data/dump-args.lua new file mode 100644 index 000000000..3772a43ec --- /dev/null +++ b/src/test/resources/test-rom/data/dump-args.lua @@ -0,0 +1 @@ +_G.__arg = _ENV.arg diff --git a/src/test/resources/test-rom/spec/programs/shell_spec.lua b/src/test/resources/test-rom/spec/programs/shell_spec.lua index cb35d70f6..fae7b85d1 100644 --- a/src/test/resources/test-rom/spec/programs/shell_spec.lua +++ b/src/test/resources/test-rom/spec/programs/shell_spec.lua @@ -6,19 +6,25 @@ describe("The shell", function() end) end) - describe("shell.run", function() - it("sets the arguments", function() - local handle = fs.open("test-files/out.txt", "w") - handle.writeLine("_G.__arg = arg") - handle.close() - - shell.run("/test-files/out.txt", "arg1", "arg2") - fs.delete("test-files/out.txt") + describe("shell.execute", function() + it("parses in arguments verbatim", function() + shell.execute("/test-rom/data/dump-args", "arg1", "arg 2") local args = _G.__arg _G.__arg = nil - expect(args):same { [0] = "/test-files/out.txt", "arg1", "arg2" } + expect(args):same { [0] = "/test-rom/data/dump-args", "arg1", "arg 2" } + end) + end) + + describe("shell.run", function() + it("tokenises the arguments", function() + shell.run("/test-rom/data/dump-args", "arg1", "arg 2") + + local args = _G.__arg + _G.__arg = nil + + expect(args):same { [0] = "/test-rom/data/dump-args", "arg1", "arg", "2" } end) end)