From 7d4a51dc665385a3df645114e9a14c4a3bfb5092 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 5 May 2026 08:01:01 +0800 Subject: [PATCH] fix(prelude): unsuppress glob in os.dirs by escaping shell metachars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `os.dirs(pattern)` used `ls -d "" 2>/dev/null` on POSIX. Wrapping the pattern in double quotes makes the shell skip glob expansion entirely — `ls -d "/tmp/x/cuda_v*"` looks for a literal file named `cuda_v*` and returns nothing. Reproduced by upstream user. Fix: replace the all-quote approach with a per-character backslash escape that preserves glob meta (`* ? [ ] ~`) while still quoting whitespace, $, backticks, and shell redirects/control chars. Now `ls -d` receives an unquoted pattern that the shell expands as expected, and paths with spaces still survive. Windows branch unchanged: cmd.exe `dir` does its own pattern matching on the argument, so the existing quoted form there is correct. --- src/lua-stdlib/prelude.lua | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/lua-stdlib/prelude.lua b/src/lua-stdlib/prelude.lua index 5ec5697..88b7ff9 100644 --- a/src/lua-stdlib/prelude.lua +++ b/src/lua-stdlib/prelude.lua @@ -100,15 +100,37 @@ os.cp = function(src, dst, opts) outf:write(content); outf:close() return true end +-- POSIX: shell-escape a glob pattern, keeping glob meta-chars raw so the +-- shell still expands them. Quoting the whole pattern (e.g. "/tmp/x/v*") +-- suppresses globbing — `ls -d "/tmp/x/v*"` would only match a file +-- literally named `v*`. Instead we backslash-escape characters that need +-- shell quoting (whitespace, $, `, ', ", redirects, etc.) and leave glob +-- meta (* ? [ ] ~) untouched. +local _SHELL_META_NO_GLOB = " \t\r\n$`'\"<>|&;()!\\" +local function _shell_glob_escape(s) + local out = {} + for i = 1, #s do + local c = s:sub(i, i) + if _SHELL_META_NO_GLOB:find(c, 1, true) then + out[#out+1] = "\\" + end + out[#out+1] = c + end + return table.concat(out) +end + os.dirs = function(pattern) local result = {} - -- Quote pattern to handle spaces; use platform-appropriate command local sep = _PATH_SEP local cmd if sep == "\\" then + -- cmd.exe `dir` does its own wildcard expansion on the pattern + -- argument, so quoting is safe and required for paths with spaces. cmd = 'dir /B /AD "' .. pattern .. '" 2>nul' else - cmd = 'ls -d "' .. pattern .. '" 2>/dev/null' + -- POSIX `ls` does NOT expand globs itself; the shell does. Escape + -- shell metachars but leave glob meta raw so expansion still works. + cmd = 'ls -d ' .. _shell_glob_escape(pattern) .. ' 2>/dev/null' end local f = io.popen(cmd) if f then