在 lua 中执行外部程序,而不需要用户输入作为参数的 lua

我想在 lua 中执行外部程序。通常可以这样做:

os.execute("run '"..arg0.."' 'arg1' arg2")

这种方法的问题在于,如果我要将用户输入作为字符串传递给外部程序,用户输入可能是 '; evil 'h4ck teh system' ',那么上面的脚本将像这样执行:

/bin/bash -c "run ''; evil 'h4ck teh system' '' 'arg1' arg2"

另一个问题是当我将 '$var' 作为参数时,shell 会用环境变量来替换它。在我的特殊情况下,我有类似 [[program 'set title "$My Title$"']] 的东西——嵌套的字符串——和 program 解析 "$My Title$"(具有转义序列)与 '$My Title$'(它就是它)的方式不同。因为我想要按原样设置标题,所以最好的方法是像这样设置参数:'My Title'。但现在命令必须是这样的:

os.execute([[run "set title '$My Title$'"]])

但现在——正如我所说的——$My 将被替换为空字符串,因为环境并不知道任何名为 $My 的变量,也因为我从未希望它被替换。

因此,我正在寻找通常的方法:

execv("run", {"set title '"..arg0.."'", arg1, arg2})
点赞
用户1847592
用户1847592
local safe_unquoted = "^[-~_/.%w%%+,:@^]*$"
local function q(text, expand)   -- quoting under *nix shells
   -- "expand"
   --    false/nil: $var and `cmd` must NOT be expanded (use single quotes)
   --    true:      $var and `cmd` must be expanded (use double quotes)
   if text == "" then
      text = '""'
   elseif not text:match(safe_unquoted) then
      if expand then
         text = '"'..text:gsub('["\\]', '\\%0')..'"'
      else
         local new_text = {}
         for s in (text.."'"):gmatch"(.-)'" do
            new_text[#new_text + 1] = s:match(safe_unquoted) or "'"..s.."'"
         end
         text = table.concat(new_text, "\\'")
      end
   end
   return text
end

function execute_commands(...)
   local all_commands = {}
   for k, command in ipairs{...} do
      for j = 1, #command do
         if not command[j]:match"^[-~_%w/%.]+$" then
            command[j] = q(command[j], command.expand)
         end
      end
      all_commands[k] = table.concat(command, " ") -- space is arguments delimiter
   end
   all_commands = table.concat(all_commands, ";")  -- semicolon is commands delimiter
   return os.execute("/bin/bash -c "..q(all_commands))
end

使用示例:

-- 使用示例#1:
execute_commands(
   {"your/program", "arg 1", "$arg2", "arg-3", "~/arg4.txt"},
   {expand=true, "echo", "Your program finished with exit code $?"},
   {"ls", "-l"}
)
-- 下面的命令将会被执行:
-- /bin/bash -c 'your/program '\''arg 1'\'' '\''$arg2'\'' arg-3 ~/arg4.txt;echo "Your program finished with exit code $?";ls -l'

由于你要求在 $arg2 周围单引号,因此 $arg2 不会扩展为值。

不幸的是,"Your program finished with exit code $?" 也不会被扩展,除非你显式设置 expand = true

-- 使用示例#2:
execute_commands{"run", "set title '$My Title$'", "arg1", "arg2"}
-- 生成的命令并不简单,但它确实做到了你需要的 :-)
-- /bin/bash -c 'run '\''set title '\''\'\'\''$My Title$'\''\'\'' arg1 arg2'
2018-11-11 07:43:53