Executable File Existence Check
Hi,
I am encountering the same issue as described here. The suggested solution there does not appear to be optimal. To determine the availability of executables in the path, I implemented the following function:
local function is_executable_in_path(executable)
local command
if wezterm.target_triple:find("linux") ~= nil then
command = "command -v " .. executable
elseif wezterm.target_triple:find("windows") ~= nil then
command = "where " .. executable
else
return false
end
return os.execute(command)
end
While this performs as expected on Linux, on Windows each call results in the spawning of a shell window. This not only disrupts the visual flow but also significantly increases startup time when multiple checks are performed.
My goal is to determine which shells are available on the system to dynamically create launch_menu entries.
I would greatly appreciate a more efficient approach to address this issue.
Thank you.
If you just want to know which shells are available on the system, you can check /etc/shells for Linux/Mac.
There doesn't seem to be a good way to do this on Windows.
Ran into similar issue where I wanted to use fish if it exists.
I have used homebrew so it install fish at /opt/homebrew/bin/fish which might not be in the path.
So I have added the following to my western config.
local function file_exists(path)
local f = io.open(path, "r")
if f~=nil then io.close(f) return true else return false end
end
if file_exists("/opt/homebrew/bin/fish") then
config.default_prog = { '/opt/homebrew/bin/fish', '-l' }
else
config.default_prog = { '/bin/bash', '-l' }
end
If I want to add it to launch menu I would also need an if else here.
{ label = "fish", args = {"/opt/homebrew/bin/fish", "-l"}
It almost seems like there should also be an easy way to add /opt/homebrew/bin to the path and have a function to see if there executable is available so one can set the args as just {"fish", "-l"}
Another option for this is use wezterm.glob to check if a specific path exists, here I'm in the debug overlay on a system that doesn't have homebrew or fish, but does have zsh at the paths I'm checking:
Debug Overlay
wezterm version: 20240812-215703-30345b36 x86_64-unknown-linux-gnu
Enter lua statements or expressions and hit Enter.
Press ESC or CTRL-D to exit
> wezterm.glob('/opt/homebrew/bin/fish')
[]
> wezterm.glob('/bin/zsh')
[
"/bin/zsh",
]
>
Glob expressions can include multiple candidates, so you can check for multiple paths at the same time. Here I'm checking for zsh, bash, fish and csh:
> wezterm.glob('/bin/{zsh,bash,fish,csh}')
[
"/bin/bash",
"/bin/zsh",
]
This was the first search result, and after finding it and going off on my own I got some results, so I'll post my findings here:
if wezterm.target_triple == 'x86_64-pc-windows-msvc' then
local function is_executable_in_path(executable)
return wezterm.run_child_process { 'where.exe', '/Q', executable }
end
local pwsh = is_executable_in_path 'pwsh.exe'
local powershell = is_executable_in_path 'powershell.exe'
local git = is_executable_in_path 'git.exe'
local elvish = is_executable_in_path 'elvish.exe'
local nu = is_executable_in_path 'nu.exe'
-- Use powershell to query the registry for the Git for Windows install path
local bash_path = ''
if git and ( pwsh or powershell ) then
local shell = pwsh and 'pwsh.exe' or 'powershell.exe'
local git_registry, git_path, stderr = wezterm.run_child_process {
shell,
'-Command',
[[(Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\GitForWindows).InstallPath]],
}
if git_registry then
for _, line in ipairs(wezterm.split_by_newlines(git_path)) do
bash_path = bash_path .. line
end
bash_path = bash_path .. [[\bin\bash.exe]]
end
end
-- Check for pwsh
if pwsh then
table.insert(launch_menu, {
label = 'PowerShell',
args = { 'pwsh.exe', '-NoLogo' },
})
config.default_prog = { 'pwsh.exe', '-NoLogo' }
if git and bash_path ~= '' then
table.insert(launch_menu, {
label = 'Git Bash',
args = { bash_path, '-i', '-l' },
})
end
table.insert(launch_menu, {
label = 'Command Prompt',
args = { os.getenv 'COMSPEC', '/k' },
})
-- No pwsh but we have Windows PowerShell
elseif powershell then
table.insert(launch_menu, {
label = 'Windows PowerShell',
args = { 'powershell.exe', '-NoLogo' },
})
config.default_prog = { 'powershell.exe', '-NoLogo' }
if git and bash_path ~= '' then
table.insert(launch_menu, {
label = 'Git Bash',
args = { bash_path, '-i', '-l' },
})
end
table.insert(launch_menu, {
label = 'Command Prompt',
args = { os.getenv 'COMSPEC', '/k' },
})
-- No powershell of any kind, just cmd.exe
else
table.insert(launch_menu, {
label = 'Command Prompt',
args = { os.getenv 'COMSPEC', '/k' },
})
config.default_prog = { os.getenv 'COMSPEC', '/k' }
end
-- Check for elvish
if elvish then
table.insert(launch_menu, {
label = 'Elvish',
args = { 'elvish.exe' },
})
end
-- Check for NuShell
if nu then
table.insert(launch_menu, {
label = 'NuShell',
args = { 'nu.exe' },
})
end
end
Some details
if wezterm.run_child_process { 'where.exe', '/Q', 'thing.exe' } thenworks just fine as a check on windows. On linux/osx you'll want to use wezterm.glob() as there is no portable solution, they all vary by shell, sincewhich/whereisvary by distribution and can't be relied upon, and the built-ins that won't screw you over occasionally vary by shell between bash/csh/dash/fish/sh/zsh/tcsh, and sometimes by version.- Use
where.exe, because you might otherwise hit the powershell built-inwherewhich functions slightly differently; where.exe is unchanged in functionality since the DOS era, the/Qflag makes it only return found/not found.
- Use
$COMSPECand%SystemRoot%\System32\cmd.exeare the canonical ways to get a DOS command line.os.getenv()will fail if the variable named doesn't exist, but if your windows install doesn't have$COMSPECyou have issues.- The call to
pwsh.exe -Commandto query the registry is extremely slow - it adds over a second to startup time - even though it is ~instantaneous in a terminal.- No idea why, the only LUA experience I've had before is neovim configs, and that was mostly setting options. I could well be doing something atrocious.
- The only other way to get a registry value from commandline is
reg.exe querywhich does not output a machine-readable format, and the way it prints varies by version and thus is very hard to parse reliably:
❯ reg.exe query "HKEY_LOCAL_MACHINE\Software\GitForWindows" /v "InstallPath" HKEY_LOCAL_MACHINE\Software\GitForWindows InstallPath REG_SZ C:\Program Files\Git - The only other way to get a registry value from commandline is
- There doesn't seem to be any mature way of interacting with the registry via Lua, except for in Lua^rt.
- No idea why, the only LUA experience I've had before is neovim configs, and that was mostly setting options. I could well be doing something atrocious.
- I'm doing the split-by-newline-and-rejoin dance because the values returned by the powershell command are newline-terminated and this is more robust than doing it manually.
- You could alternatively run
where.exe git.exe(no/Qflag) and strip thecmd\git.exeat the end of the second (fully qualified path) return value. This will be much faster but also far more likely to break.
wez, if you're getting the WSL entries via registry, maybe you can expose the query method? That or auto-populate a Git-Bash entry, I imagine the intersection of "Windows users who use WezTerm" and "Windows users who use Git" is significant.