Skip to content

Commit 52491a4

Browse files
authored
Merge pull request #94 from BirdeeHub/pre-post-run
feat(run_cmd): post_5_2_run and pre_5_2_run deprecated for run_cmd
2 parents 3ee1e53 + 3086bb0 commit 52491a4

3 files changed

Lines changed: 119 additions & 120 deletions

File tree

README.md

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![Lua][lua-shield]][lua-url]
44
[![LuaRocks][luarocks-shield]][luarocks-url]
55

6-
Tiny library for shell scripting with Lua (inspired by zserge/luash).
6+
Tiny library for shell scripting with Lua (inspired by [zserge/luash](https://github.com/zserge/luash)).
77

88
`luash` is interesting, but it modifies `_G` in an extreme way.
99

@@ -20,15 +20,6 @@ It works with any "posix-enough" shell by default such as `bash`, `zsh`, and `da
2020

2121
But it will not work by default with `fish`, `nushell`, `cmd` or `powershell` unless you define a [representation](./REPR.md) for that shell.
2222

23-
It also exports a [small nix helper](#in-addition-to-the-library) that allows you
24-
to use `shelua` to write `nix` derivations in `lua` instead of `bash`.
25-
26-
It is `pkgs.runCommand` except it is `pkgs.runLuaCommand` because the command is in `lua`.
27-
28-
It is useful when you have a short build or wrapper script that needs to deal with a lot of structured data.
29-
30-
Especially when you have a lot of `json` and would rather use `cjson` and deal with tables than use `jq` and bash arrays
31-
3223
## Install
3324

3425
via luarocks: `luarocks install shelua`
@@ -39,7 +30,7 @@ Or just clone this repo and copy `lua/sh.lua` into your project.
3930

4031
## Simple usage
4132

42-
Every command that can be called via os.execute can be called via the sh table.
33+
Every command that can be called via `os.execute` can be called via the sh table.
4334
All the arguments passed into the function become command arguments.
4435

4536
``` lua
@@ -56,7 +47,7 @@ end
5647
## Command input and pipelines
5748

5849
If command argument is a table which has a `__input` field - it will be used as
59-
a command input (stdin). Multiple arguments with input are allowed, they will
50+
a command input (`stdin`). Multiple arguments with input are allowed, they will
6051
be concatenated.
6152

6253
The each command function returns a structure that contains the `__input`
@@ -84,8 +75,8 @@ sh.ls '/bin' : grep "$filter" : wc '-l'
8475
```
8576

8677
Note that the commands are not running in parallel (because Lua can only handle
87-
one I/O loop at a time). So the inner-most command is executed, its output is
88-
read, the the outer command is execute with the output redirected etc.
78+
one `I/O` loop at a time). So the inner-most command is executed, its output is
79+
read, the outer command is execute with the output redirected etc.
8980

9081
However, `shelua` also offers a `proper_pipes` [setting](#settings).
9182

@@ -116,7 +107,7 @@ arguments without manually changing the arguments list.
116107
## Partial commands and commands with tricky names or characters
117108

118109
You can call `sh` with a string as the first argument to construct a command function, optionally
119-
pre-setting the arguments:
110+
presetting the arguments:
120111

121112
``` lua
122113
local sh = require('sh')

REPR.md

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@ Its first return value will then be passed through any defined `transforms`.
7878

7979
the result, in addition to its optional second return value will then be passed to one of the 2 following run functions based on current lua version.
8080

81-
`post_5_2_run` and `pre_5_2_run` are what call the actual final shell command when needed.
81+
`run_cmd` is what calls the actual final shell command when needed.
8282

83-
Their job is to run the command and report the result, exit and signal codes.
83+
Its job is to run the command and report the result, exit and signal codes.
8484

85-
Prior to 5.2 the io.popen command does not return exit code or signal. You can decide to support older than 5.2 or not.
85+
Prior to 5.2 the `io.popen` command does not return exit code or signal. You can decide to support older than 5.2 or not.
8686

8787
```lua
88-
---returns cmd and an optional item such as path to a tempfile to be passed to post_5_2_run or pre_5_2_run
88+
---returns cmd and an optional item such as path to a tempfile to be passed to run_cmd
8989
---called only when proper_pipes is false
9090
---cmd is the result of add_args
9191
---codes is the list of codes that correspond with each input such as `__exitcode`, empty if none
@@ -105,53 +105,50 @@ Prior to 5.2 the io.popen command does not return exit code or signal. You can d
105105
end,
106106
---runs the command and returns the result and exit code and signal
107107
-- cmd is the result of single_stdin or concat_cmd, after being passed through any defined transforms
108-
---@field post_5_2_run fun(opts: Shelua.Opts, cmd: string|any, msg: any?): { __input: string, __exitcode: number, __signal: number }
109-
post_5_2_run = function(opts, cmd, tmp)
110-
local p = io.popen(cmd, 'r')
111-
local output, exit, status
112-
if p then
113-
output = p:read('*a')
114-
_, exit, status = p:close()
115-
end
116-
pcall(os.remove, tmp)
108+
---@field run_cmd fun(opts: Shelua.Opts, cmd: string|any, msg: any?): { __input: string, __exitcode: number, __signal: number }
109+
run_cmd = function(opts, cmd, tmp)
110+
if is_5_2_plus then
111+
local p = io.popen(cmd, 'r')
112+
local output, _, exit, status
113+
if p then
114+
output = p:read('*a')
115+
_, exit, status = p:close()
116+
end
117+
pcall(os.remove, tmp)
117118

118-
return {
119-
__input = output,
120-
__exitcode = exit == 'exit' and status or 127,
121-
__signal = exit == 'signal' and status or 0,
122-
}
123-
end,
124-
---runs the command and returns the result and exit code and signal
125-
---Should return the flags using the same format as io.popen does in 5.2+
126-
-- cmd is the result of single_stdin or concat_cmd, after being passed through any defined transforms
127-
---@field pre_5_2_run fun(opts: Shelua.Opts, cmd: string|any, msg: any?): { __input: string, __exitcode: number, __signal: number }
128-
pre_5_2_run = function(opts, cmd, tmp)
129-
local p = io.popen(cmd .. "\necho __EXITCODE__$?", 'r')
130-
local output
131-
if p then
132-
output = p:read('*a')
133-
p:close()
119+
return {
120+
__input = output,
121+
__exitcode = exit == 'exit' and status or 127,
122+
__signal = exit == 'signal' and status or 0,
123+
}
124+
else
125+
local p = io.popen(cmd .. "\necho __EXITCODE__$?", 'r')
126+
local output
127+
if p then
128+
output = p:read('*a')
129+
p:close()
130+
end
131+
pcall(os.remove, tmp)
132+
local exit
133+
output = (output or ""):gsub("__EXITCODE__(%d*)\r?\n?$", function(code)
134+
exit = tonumber(code)
135+
return ""
136+
end)
137+
return {
138+
__input = output,
139+
__exitcode = exit or 127,
140+
__signal = (exit and exit > 128) and (exit - 128) or 0
141+
}
134142
end
135-
pcall(os.remove, tmp)
136-
local exit
137-
output = (output or ""):gsub("__EXITCODE__(%d*)\r?\n?$", function(code)
138-
exit = tonumber(code)
139-
return ""
140-
end)
141-
return {
142-
__input = output,
143-
__exitcode = exit or 127,
144-
__signal = (exit and exit > 128) and (exit - 128) or 0
145-
}
146143
end,
147-
---if your pre_5_2_run or post_5_2_run returns a table with extra keys, e.g. `__stderr`
144+
---if your run_cmd returns a table with extra keys, e.g. `__stderr`
148145
---proper_pipes will need to know that accessing them should be a trigger to resolve the pipe.
149146
---each string in this table must begin with '__' or it will be ignored
150147
---@field extra_cmd_results string[]|fun(opts: Shelua.Opts): string[]
151148
extra_cmd_results = {},
152149
---a list of functions to run in order on the command before running it.
153150
---each one recieves the previous value and returns a new one.
154-
---they are ran after concat_cmd or single_stdin and before the post_5_2_run and pre_5_2_run functions
151+
---they are ran after concat_cmd or single_stdin and before the run_cmd functions
155152
---@field transforms? (fun(cmd: string|any): string|any)[]
156153
transforms = {},
157154
```
@@ -172,7 +169,7 @@ which is the optional second return value of the call to `concat_cmd`
172169
Your goal in this function is to construct a string from the prior inputs,
173170
that pipes them into the command, and then return that string, if there are any prior inputs to pipe.
174171

175-
Its result will be provided to the same run function as `single_stdin` would have, either `pre_5_2_run` or `post_5_2_run`,
172+
Its result will be provided to the same run function as `single_stdin` would have, `run_cmd`,
176173
after adding the newly resolved values to the command result being resolved.
177174

178175
```lua
@@ -192,7 +189,7 @@ after adding the newly resolved values to the command result being resolved.
192189

193190
---strategy to combine piped inputs, 0, 1, or many, return resolved command to run
194191
---called only when proper_pipes is true
195-
---may return an optional second value to be placed in another PipeInput, or returned to post_5_2_run or pre_5_2_run
192+
---may return an optional second value to be placed in another PipeInput, or returned to run_cmd
196193
-- cmd is the same type as the result of add_args
197194
---@field concat_cmd fun(opts: Shelua.Opts, cmd: string|any, input: Shelua.PipeInput[]): (string|any, any?)
198195
concat_cmd = function(opts, cmd, input)

lua/sh.lua

Lines changed: 72 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
---Will contain either `s`, a plain string,
22
---or `c`, an input command string
3-
---@class Shelua.PipeInput
3+
---@class Shelua.PipeInputStdin
44
---string stdin to combine
55
---@field s? string|any
66
---if string input came from a command,
77
---`e` will contain a table of all other command result fields
88
---such as `__exitcode`
99
---@field e? table
10+
11+
---@class Shelua.PipeInputClass
1012
---cmd to combine
1113
---@field c? string|any
1214
---optional 2nd return of concat_cmd
1315
---@field m? any
1416

17+
---@alias Shelua.PipeInput Shelua.PipeInputStdin | Shelua.PipeInputClass
18+
1519
---@class Shelua.Repr
1620
---escapes a string for the shell
1721
---@field escape fun(arg: any, opts: Shelua.Opts?): string
@@ -20,7 +24,7 @@
2024
---@field arg_tbl fun(opts: Shelua.Opts, k: string, a: any): string|string[]?
2125
---adds args to the command
2226
---@field add_args fun(opts: Shelua.Opts, cmd: string, args: string[]): string|any
23-
---returns cmd and an optional item such as path to a tempfile to be passed to post_5_2_run or pre_5_2_run
27+
---returns cmd and an optional item such as path to a tempfile to be passed to run_cmd
2428
---called when proper_pipes is false
2529
---cmd is the result of add_args
2630
---codes is the list of codes that correspond with each input such as `__exitcode`, empty if none
@@ -30,13 +34,11 @@
3034
---@field concat_cmd fun(opts: Shelua.Opts, cmd: string|any, input: Shelua.PipeInput[]): (string|any, any?)
3135
---a list of functions to run in order on the command before running it.
3236
---each one recieves the previous value and returns a new one.
33-
---they are ran after concat_cmd or single_stdin and before the post_5_2_run and pre_5_2_run functions
37+
---they are ran after concat_cmd or single_stdin and before the run_cmd functions
3438
---@field transforms? (fun(cmd: string|any): string|any)[]
3539
---runs the command and returns the result and exit code and signal
36-
---@field post_5_2_run fun(opts: Shelua.Opts, cmd: string|any, msg: any?): { __input: string, __exitcode: number, __signal: number }
37-
---runs the command and returns the result and exit code and signal
38-
---@field pre_5_2_run fun(opts: Shelua.Opts, cmd: string|any, msg: any?): { __input: string, __exitcode: number, __signal: number }
39-
---if your pre_5_2_run or post_5_2_run returns a table with extra keys, e.g. `__stderr`
40+
---@field run_cmd fun(opts: Shelua.Opts, cmd: string|any, msg: any?): { __input: string, __exitcode: number, __signal: number }
41+
---if your run_cmd returns a table with extra keys, e.g. `__stderr`
4042
---proper_pipes will need to know that accessing them should be a trigger to resolve the pipe.
4143
---each string in this table must begin with '__' or it will be ignored
4244
---@field extra_cmd_results string[]|fun(opts: Shelua.Opts): string[]
@@ -134,10 +136,34 @@ local function tbl_get(t, default, ...)
134136
return t or default
135137
end
136138

139+
local warned_run_cmd_shim = false
140+
137141
---@param opts Shelua.Opts
138142
---@param attr string
139143
---@return function
140144
local get_repr_fn = function(opts, attr)
145+
if attr == "run_cmd" then
146+
local shell = opts.shell or "posix"
147+
local shell_repr = tbl_get(opts, nil, "repr", shell)
148+
if shell_repr and not shell_repr.run_cmd then
149+
local old_post = shell_repr.post_5_2_run
150+
local old_pre = shell_repr.pre_5_2_run
151+
if old_post or old_pre then
152+
if not warned_run_cmd_shim then
153+
warned_run_cmd_shim = true
154+
io.stderr:write("shelua: post_5_2_run/pre_5_2_run are deprecated. ")
155+
io.stderr:write("Use run_cmd(opts, cmd, msg) instead.\n")
156+
end
157+
shell_repr.run_cmd = function(o, cmd, msg)
158+
if is_5_2_plus and old_post then
159+
return old_post(o, cmd, msg)
160+
else
161+
return (old_pre or old_post)(o, cmd, msg)
162+
end
163+
end
164+
end
165+
end
166+
end
141167
return tbl_get(opts, tbl_get(opts, function()
142168
error("Shelua Repr Error: " ..
143169
tostring(attr) .. " function required for shell: " .. tostring(opts.shell or "posix"))
@@ -222,39 +248,40 @@ local posix = {
222248
return cmd
223249
end
224250
end,
225-
post_5_2_run = function(opts, cmd, tmp)
226-
local p = io.popen(cmd, 'r')
227-
local output, _, exit, status
228-
if p then
229-
output = p:read('*a')
230-
_, exit, status = p:close()
231-
end
232-
pcall(os.remove, tmp)
251+
run_cmd = function(opts, cmd, tmp)
252+
if is_5_2_plus then
253+
local p = io.popen(cmd, 'r')
254+
local output, _, exit, status
255+
if p then
256+
output = p:read('*a')
257+
_, exit, status = p:close()
258+
end
259+
pcall(os.remove, tmp)
233260

234-
return {
235-
__input = output,
236-
__exitcode = exit == 'exit' and status or 127,
237-
__signal = exit == 'signal' and status or 0,
238-
}
239-
end,
240-
pre_5_2_run = function(opts, cmd, tmp)
241-
local p = io.popen(cmd .. "\necho __EXITCODE__$?", 'r')
242-
local output
243-
if p then
244-
output = p:read('*a')
245-
p:close()
261+
return {
262+
__input = output,
263+
__exitcode = exit == 'exit' and status or 127,
264+
__signal = exit == 'signal' and status or 0,
265+
}
266+
else
267+
local p = io.popen(cmd .. "\necho __EXITCODE__$?", 'r')
268+
local output
269+
if p then
270+
output = p:read('*a')
271+
p:close()
272+
end
273+
pcall(os.remove, tmp)
274+
local exit
275+
output = (output or ""):gsub("__EXITCODE__(%d*)\r?\n?$", function(code)
276+
exit = tonumber(code)
277+
return ""
278+
end)
279+
return {
280+
__input = output,
281+
__exitcode = exit or 127,
282+
__signal = (exit and exit > 128) and (exit - 128) or 0
283+
}
246284
end
247-
pcall(os.remove, tmp)
248-
local exit
249-
output = (output or ""):gsub("__EXITCODE__(%d*)\r?\n?$", function(code)
250-
exit = tonumber(code)
251-
return ""
252-
end)
253-
return {
254-
__input = output,
255-
__exitcode = exit or 127,
256-
__signal = (exit and exit > 128) and (exit - 128) or 0
257-
}
258285
end,
259286
extra_cmd_results = {},
260287
transforms = {},
@@ -362,21 +389,14 @@ local cmd_mt = {
362389
end
363390
if check_if_cmd_result(opts, c) then
364391
local apply = function(com)
365-
local transforms = opts.transforms
366-
if transforms then print("Shelua Deprecation: transforms option moved to be a repr-specific option") end
367-
transforms = tbl_get(opts, transforms or {}, "repr", opts.shell or "posix", "transforms")
392+
local transforms = tbl_get(opts, {}, "repr", opts.shell or "posix", "transforms")
368393
for _, f in ipairs(transforms) do
369394
com = f(com)
370395
end
371396
return com
372397
end
373398
local cmd, msg = resolve(self, opts)
374-
local res
375-
if is_5_2_plus then
376-
res = get_repr_fn(opts, "post_5_2_run")(opts, apply(cmd), msg)
377-
else
378-
res = get_repr_fn(opts, "pre_5_2_run")(opts, apply(cmd), msg)
379-
end
399+
local res = get_repr_fn(opts, "run_cmd")(opts, apply(cmd), msg)
380400
for k, v in pairs(res or {}) do
381401
rawset(self, k, v)
382402
end
@@ -494,25 +514,16 @@ command = function(self, cmdstr, ...)
494514
unresolved[t] = { cmd = cmd, unres = unres, input = input, codes = codes }
495515
else
496516
local apply = function(com)
497-
local transforms = shmt.transforms
498-
if transforms then print("Shelua Deprecation: transforms option moved to be a repr-specific option") end
499-
transforms = tbl_get(shmt, transforms or {}, "repr", shmt.shell or "posix", "transforms")
517+
local transforms = tbl_get(shmt, {}, "repr", shmt.shell or "posix", "transforms")
500518
for _, f in ipairs(transforms) do
501519
com = f(com)
502520
end
503521
return com
504522
end
505-
if is_5_2_plus then
506-
local msg
507-
cmd, msg = get_repr_fn(shmt, "single_stdin")(shmt, cmd, #input > 0 and input or nil,
508-
#codes > 0 and codes or nil)
509-
t = get_repr_fn(shmt, "post_5_2_run")(shmt, apply(cmd), msg)
510-
else
511-
local msg
512-
cmd, msg = get_repr_fn(shmt, "single_stdin")(shmt, cmd, #input > 0 and input or nil,
513-
#codes > 0 and codes or nil)
514-
t = get_repr_fn(shmt, "pre_5_2_run")(shmt, apply(cmd), msg)
515-
end
523+
local msg
524+
cmd, msg = get_repr_fn(shmt, "single_stdin")(shmt, cmd, #input > 0 and input or nil,
525+
#codes > 0 and codes or nil)
526+
t = get_repr_fn(shmt, "run_cmd")(shmt, apply(cmd), msg)
516527
if shmt.assert_zero and t.__exitcode ~= 0 then
517528
error("Command " .. tostring(cmd) .. " exited with non-zero status: " .. tostring(t.__exitcode))
518529
end

0 commit comments

Comments
 (0)