- Lua offers lightweight, portability, and advanced control (coroutines, first-class functions) compared to Bash's orchestration approach.
- Bash excels at pipes and POSIX utilities; Lua excels when the logic is complex or cross-platform.
- Mako Server and luash extend Lua for practical automation (os.execute, io.popen) without losing simplicity.
- Errors like exit 127 are usually due to inconsistent aliases/paths; check with type -ay for absolute paths.

Automation and scripting are commonplace in Unix and Unix-like systems, but also in mixed environments with Windows. The eternal comparison between Bash and Lua It arises when we seek balance between speed, portability and ease of maintenance.
Based on what the best-positioned sources show - articles that put to Lua as a versatile alternative to Bash, practical guides with Mako Server, forum discussions and pro-Bash opinions—, we are going to organize ideas, get down to examples and point out common traps such as the dreaded exit 127 when invoking Lua from Bash on Windows (Git Bash). We'll also provide context with references to other languages listed in the sources (JavaScript, Python, Node.js, HTML5, PHP) and the familiar Reddit cookie warning that many see when opening threads.
What are Bash and Lua
In a nutshell, Bash (Bourne Again SHell) is the shell par excellence for Unix-like systems: it interprets commands, orchestrates utilities and allows processes to be chained using pipes and redirections. Its central objective It is a powerful command interpreter with a programming layer to combine them.
Moon It is a lightweight, fast, embeddable, cross-platform language with clean syntax. Its minimalist design It allows you to run on limited resources and within applications (game engines, networking tools, embedded servers), offering modern control structures and a single composite data type: the table.
- Bash: ideal for text processing, file manipulation and system administration taking advantage of the entire ecosystem of POSIX utilities.
- Moon: general-purpose, embeddable language; offers more expressiveness and control for complex logic, while maintaining a small footprint and high portability.
Key differences between scripting and automation
Several pieces of the ranking insist on this: Lua stands out for its lightness and speedIts VM is compact and the interpreter starts quickly, which is appreciated in scripts that run many times or in embedded environments.
Portability is another strong point. Lua is not tied to Unix: runs smoothly on Linux, macOS, and Windows, minimizing ifs per platform. Bash, although usable on Windows via emulations (Git Bash, WSL), it depends more of POSIX tools and environment nuances.
As for expressiveness, Lua offers first-class features, coroutines, and metatables. These pieces allow you to build larger solutions without resorting to external utilities, while in Bash it's common to build the solution with sed, awk, grep, find, etc. Bash shines precisely in that ecosystem and in working with pipes.
Learning and readability
Several guides point out that the syntax of Lua looks like pseudocode, which helps beginners. Bash, on the other hand, has quoting, expansion, and substitution rules that may surprise you. Two equivalent examples (loop 1 to 5):
# Bash
#!/usr/bin/env bash
for i in {1..5}; do
echo 'Hola, mundo '"$i"
done
-- Lua
for i = 1, 5 do
print('Hola, mundo ' .. i)
end
the version in Lua is direct and less prone to errors caused by quoting or unusual expansion. In Bash, mastering when to use single and double quotes and how to expand variables or braces is crucial.
Advanced Lua Features for Automation
First class features: They are stored in variables, passed as arguments, and returned; they promote modularity and reuse in medium-to-high-complexity automation.
Coroutines: facilitate non-blocking concurrent tasks without dealing with OS threads. Very useful for coordinate flows (e.g. multiple I/O jobs) without additional complexity.
Metatables and metamethods: allow you to redefine the behavior of operations on tables, spreading the language to adapt it to the problem domain.
A simple and oft-cited example in articles: line-by-line file processing in Lua, concisely and clearly:
-- Lua: leer y procesar un archivo línea a línea
local function procesar(archivo)
local f = io.open(archivo, 'r')
for linea in f:lines() do
print('Procesando: ' .. linea)
end
f:close()
end
procesar('ejemplo.txt')
This pattern shows how Lua combines simple I/O with clarity; it is easy to extend to parse, filter, or communicate with other components.
Lua in Practice: Mako Server for Modern Scripting
One of the best-rated guides suggests using Mako Server to extend Lua with automation-oriented APIs (including networking), reducing dependency on external packages. The shebang typical would be:
#!/usr/bin/env mako
print('Hola Lua!')
Installation on Linux x86‑64 (according to tutorial): fast and hassle-free.
cd /tmp
wget makoserver.net/download/mako.linux-x64.tar.gz
tar xvzf mako.linux-x64.tar.gz
sudo cp mako mako.zip /usr/local/bin/
Once installed, the script runs normally and take advantage of extra APIs from Mako. For example, creating routes and listing content with system calls:
#!/usr/bin/env mako
os.execute('mkdir -p api/os api/fs')
os.execute('ls -lh api')
If you need Bash features (e.g., brace expansion), you can invoke it from Lua and maintain ergonomics:
#!/usr/bin/env mako
local function ex(cmd)
-- Usa bash como login shell y escapa el comando
return os.execute('/bin/bash -lc ' .. string.format('%q', cmd))
end
ex('mkdir -p api/{os,fs}')
ex('ls -lh api')
To capture standard output (and not just the exit code), io.popen It's very useful. A common pattern is to get binary versions:
#!/usr/bin/env mako
local function ex(cmd)
local p = io.popen(cmd)
local out = p:read('*a') or ''
p:close()
return (out:gsub('%s+$',''))
end
local function version(s)
return s:match('([%d]+%.[%d]+%.[%d]+)')
end
local nodev = version(ex('node --version'))
print('node version\t' .. (nodev or 'not installed'))
local wgetv = version(ex('wget -V'))
print('wget version\t' .. (wgetv or 'not installed'))
So you build simple dependency checks without leaving Lua, maintaining a clear and portable flow.
External packages with Mako: example with luash
Another popular recommendation is luash, a lightweight library that simplifies launching system commands from Lua with a shell-like API. Typical installation:
git clone https://github.com/zserge/luash.git
sudo mkdir -p /usr/local/share/lua/5.4/
sudo cp luash/sh.lua /usr/local/share/lua/5.4/
A short script listing the current directory would look like this, mixing idiomatic Lua with shell calls:
#!/usr/bin/env mako
require('sh')
local cwd = tostring(pwd())
print('Files in ' .. cwd)
local listing = tostring(ls('.'))
for f in listing:gmatch('[^\n]+') do
print(f)
end
The idea is to minimize manual glue when what you want is invoke external tools and process results with the convenience of Lua.
Bash or Lua? Practical selection criteria
A recurring opinion in forums is that criticize Bash for not being a general-purpose language is to lose focus. Bash is designed as a command interpreter for composing system utilities; its language is used to glue them together using pipes or IPC.
If your work is very console-based, you use anonymous pipes, redirects, grep/sed/awk on a daily basis and your logic is less than external orchestration, Bash is still the best Swiss Army knife. Does not compete against Lua as a general-purpose language; composes commands like no one else.
If your case requires elaborate flow control, data transformation, coroutines or strict portability (Linux/macOS/Windows) without relying on POSIX utilities, Lua gives a more comfortable experience than avoid pain of quoting and complex expansions.
It's also worth remembering the ecosystem: with Bash you can pull from the entire *utils universe (coreutils, binutils, util‑linux…). And if you miss specific data structures, there are tools like recutils that complement the traditional textual model.
Diagnosing common errors: the case of exit 127
In a popular thread, when running a script like bash bb.sh a appeared exit code 127 after trying lua -e 'print «hha»', while with source bb.sh Everything was going well. In POSIX, 127 means “command not found".
Si moon is mapped to something else (for example, in the thread it looked lua is aliased to `lua53`), you may find that when launching a new process with bash that alias or route does not exist or points to a binary not present in the effective PATH of that subshell, causing 127.
In Windows environments with Git Bash another factor comes into play: from cmd.exe It is common to define doskey (e.g., doskey lua=lua53 $*). If the Git Bash session inherits anything from the environment that resolve lua to lua53, but in that subshell there is no lua53 or its path, you will get 127; when doing source, instead, the current shell is used with your own PATH/alias and it can work.
# Comprobar resolución del binario
type -a lua
# Sugerencias:
# 1) Invocar la ruta absoluta: /usr/local/bin/lua o /c/Program Files/lua/lua
# 2) Desactivar alias: unalias lua; o usa 'command lua' para saltarte funciones/alias
# 3) Limpia el hash de rutas en bash: hash -r
# 4) Revisa PATH en el subshell que crea 'bash bb.sh'
The solution in the case of the thread was to remove the doskey lua=lua53 $* in cmd, after which bash lua He started behaving normally again. Moral: If you see a 127 suspect, check aliases, shell functions and PATH; and verify with type -a o command -v which binary is actually being attempted to execute.
Context: Other languages and tools that appear in the sources
The results contain descriptions of JavaScript (not only in browser, also Node.js/Apache CouchDB) and Python (clear syntax, generalist). These languages usually compete for the gap from automation scripting in teams that come from the development side.
Node.js offers runtime with non-blocking I/O designed for real time, while HTML5 y PHP They appear as historical pieces of web development on some indexed pages. They are not direct rivals of Bash or Lua in system administration, but alternatives for multiplatform automations.
Modern Python utilities are mentioned in discussions such as uv to resolve dependencies without mounting heavy projects, or alternative shells like xonsh (Python-powered), plus options like Racket, D in script mode, or OCaml. The practical conclusion: Choose the tool based on the case and your team's friction with each ecosystem.
It is worth remembering that social platforms such as Reddit They display privacy notices and cookies before viewing the content; this doesn't affect the technique, but it explains why sometimes threads are not loading without accepting the notice.
If you need scripts that work the same on Linux, macOS and Windows, with rich logic, tests and swift executions, Lua is a great candidate. If you're going to orchestrate POSIX utilities, glue tools together with pipes, and do glue scripting With minimal friction, Bash remains unbeatable. The good news is that they're not mutually exclusive: combining Lua (or Lua+Mako) for the "logical" part and Bash for the "shell glue" gives you the best of both worlds.
Table of Contents
- What are Bash and Lua
- Key differences between scripting and automation
- Learning and readability
- Advanced Lua Features for Automation
- Lua in Practice: Mako Server for Modern Scripting
- External packages with Mako: example with luash
- Bash or Lua? Practical selection criteria
- Diagnosing common errors: the case of exit 127
- Context: Other languages and tools that appear in the sources