Using LLMs as Script Interpreters: Shebang Lines Turn Natural Language into Executable Scripts

Using the shebang mechanism to make plain English text files executable via LLM tools
Simon Willison demonstrates a technique of embedding LLM command-line tools into Unix shebang lines, turning plain English natural language text files into executable scripts. By leveraging the env -S parameter to overcome shebang multi-argument limitations, combined with LLM's fragments feature and tool-calling capabilities, users can describe tasks in natural language and run them directly — even integrating Python functions, database queries, and other complex operations, illustrating the ongoing evolution of programming abstraction from machine code to natural language.
When Plain English Text Files Become Executable
If you're a Linux/macOS user, you're no doubt familiar with #!/bin/bash or #!/usr/bin/env python3 at the first line of a script file — this is the shebang line, which tells the operating system which interpreter to use to execute the script.
The shebang (#!) mechanism is a kernel-level feature of Unix/Linux. When the operating system's execve system call detects that a file begins with #!, it parses the interpreter path specified on that line and passes the file itself as an argument to that interpreter for execution. This mechanism dates back to Dennis Ritchie's implementation in Version 8 Unix in 1980 — 45 years ago. It's worth noting that the shebang line has length limits on different systems (the Linux kernel limits it to 128 bytes), and traditionally supports only a single argument — a limitation that becomes critically important later in this article.
Now, Simon Willison (the creator of Datasette) has demonstrated a bold idea: putting an LLM command-line tool in the shebang line, turning plain English natural language text files into executable scripts. You write a description in English, grant the file execute permissions, run it directly, and the LLM carries out the task for you.
The inspiration came from a comment by Hacker News user Kim_Bruning:
"Seriously, you can now put a shebang on an English text file (if you're brave enough)."
The Simplest Form: Natural Language as Script
The most basic usage leverages the fragments feature of the LLM tool. Create a file with the following content:
#!/usr/bin/env -S llm -f
Generate an SVG of a pelican riding a bicycle
Grant the file execute permissions (chmod +x), then run it directly, and the LLM will generate an SVG image of a pelican riding a bicycle. The entire file, aside from the shebang line, is a single sentence of plain English.
The key here is the env -S parameter — it allows multiple arguments to be passed to the specified program. The traditional shebang mechanism has a well-known limitation: everything after #! is treated as a single argument passed to the interpreter. For example, #!/usr/bin/env python3 -u on some systems treats python3 -u as one single argument, causing execution to fail. The env -S (split-string) option, introduced in GNU coreutils 8.30 (released in 2018), solves this problem by splitting the subsequent string into multiple independent arguments by spaces. This seemingly minor improvement is exactly what provides the technical foundation for LLM shebang scripts — without it, you couldn't pass both the llm command and the -f flag in a shebang line.
The -f flag tells LLM to treat the remaining file content as a fragment (a prompt fragment). LLM is an open-source command-line tool developed by Simon Willison that supports calling OpenAI, Anthropic, local models, and various other large language models through a unified interface. Fragments were introduced in LLM version 0.26, allowing content to be read from files, URLs, or standard input as part of the prompt via the -f flag. In the shebang scenario, when the -f flag is used without additional arguments, it reads the remaining content of the script file itself (everything after the shebang line) as a fragment — this is the core mechanism that makes natural language scripts work.
Advanced Usage: Integrating Tool Calls
The power of the LLM command-line tool lies in its ability to not just generate text, but also call tools. Using the -T parameter, you can enable tool-calling capabilities in shebang scripts:
#!/usr/bin/env -S llm -T llm_time -f
Write a haiku that mentions the exact current time
This script calls the llm_time tool to get the current time, then generates a haiku that includes the precise time. The LLM autonomously decides when to call a tool and how to incorporate the tool's returned results into the final output.
Tool calling (also known as Function Calling) is one of the key capabilities of modern large language models, first introduced by OpenAI in June 2023 with the GPT-3.5/GPT-4 API. The workflow goes like this: developers describe the names, parameters, and capabilities of available tools to the model; during inference, the model determines whether a tool call is needed, and if so, generates a structured call request (including the function name and arguments); the client executes the actual call and returns the result to the model; the model then continues generating the final answer based on the tool's returned results. This mechanism allows LLMs to break beyond their own limitations — such as precise mathematical calculations, accessing real-time information, or querying external databases — while maintaining the fluidity of natural language interaction. Anthropic, Google, and other providers have since implemented similar tool-calling protocols.
Power User: YAML Templates + Custom Python Functions
More complex scenarios can use the YAML template format, defining Python functions directly in the script as tools:
#!/usr/bin/env -S llm -t
model: gpt-5.4-mini
system: |
Use tools to run calculations
functions: |
def add(a: int, b: int) -> int:
return a + b
def multiply(a: int, b: int) -> int:
return a * b
Save it as calc.sh and execute:
./calc.sh 'what is 2344 * 5252 + 134' --td
--td is the tool debugging option, which lets you see the LLM's complete reasoning process:
Tool call: multiply({'a': 2344, 'b': 5252})
12310688
Tool call: add({'a': 12310688, 'b': 134})
12310822
2344 × 5252 + 134 = **12,310,822**
The LLM correctly decomposed the mathematical expression into two steps — multiply first, then add — and obtained precise results through tool calls rather than relying on its own mathematical computation abilities. This pattern cleverly combines the LLM's language understanding capabilities with the precise execution of deterministic tools. This is also an important paradigm in current AI application design: let the LLM handle understanding intent and orchestrating workflows, and let traditional code handle precise execution — leveraging the strengths of each.
Practical Application: Querying Databases with Natural Language
Simon also demonstrated an example closer to everyday work: querying blog content with natural language through the Datasette SQL API. You can write a script that asks in English, "How many articles about AI did I write last year?" and the LLM will automatically construct a SQL query, call the API, parse the results, and return a human-readable answer.
Datasette is another well-known open-source project by Simon Willison. It can instantly publish SQLite databases as interactive web applications and JSON APIs. Datasette's design philosophy is to make data exploration and publishing extremely simple — just a single SQLite file is all you need to launch a complete data browsing, querying, and API service. Its SQL API allows executing arbitrary SQL queries via HTTP requests and receiving results in JSON format. In the natural language script scenario, the LLM acts as a translation layer between user intent and SQL queries: the user describes their needs in English, the LLM understands the semantics and generates the corresponding SQL statement, executes the query through a tool call to the Datasette API, and finally converts the structured results into a human-readable natural language answer.
This pattern essentially turns the LLM into a universal natural language interface layer, connecting to various backend tools and data sources.
What Natural Language Scripts Mean
From a technical standpoint, this trick isn't particularly complex — it simply makes clever use of Unix's shebang mechanism and the argument design of the LLM CLI tool. But the signal it sends is worth paying attention to:
The abstraction level of programming is changing. From machine code to assembly, from assembly to high-level languages, from high-level languages to scripting languages, and now natural language is beginning to become an "executable" form of expression. Looking back through history: in the 1940s, programmers wrote binary machine code directly; in the 1950s, assembly language introduced mnemonics; in 1957, Fortran pioneered the era of high-level languages, letting programmers write in expressions close to mathematics; in the 1980s-90s, scripting languages like Perl and Python further lowered the barrier to programming, emphasizing rapid development and readability; in the 2010s, low-code/no-code platforms attempted to replace text-based programming with visual approaches. Each elevation in abstraction level has been accompanied by a partial surrender of precise control — assembly programmers once questioned the efficiency of compiler-generated code, and C programmers once questioned the performance of scripting languages.
Of course, the non-deterministic output of LLMs means you wouldn't want to rely on this approach in a production environment — at least not yet. The biggest challenge facing natural language as a programming interface is precisely this non-determinism: the same English instruction may produce different outputs, creating a fundamental tension with the deterministic execution of traditional programming.
But as a tool for rapid prototyping, one-off tasks, or personal workflow automation, "natural language scripts" genuinely lower the barrier to interacting with computers. You don't need to remember curl's argument format, you don't need to look up jq syntax — you just describe what you want in English.
As Kim_Bruning said, you just need to be "brave enough."
Related articles
TutorialsChatGPT Plus Subscription Guide: Are GPT-5.5, image-2, and Codex Worth the Upgrade?
A detailed look at ChatGPT Plus features — GPT-5.5, image-2, and Codex — with a Plus vs Pro comparison and a complete step-by-step subscription guide for users outside the US.
TutorialsHarness AI Engineering in Practice: Using Claude Code to Master Enterprise-Level E-Commerce Development
Deep dive into Harness AI Engineering: master enterprise e-commerce development with Claude Code using the Rules, Skills, Wiki, and Changes framework.
TutorialsCursor + Codex Dual-IDE Collaboration: A Practical Methodology for Open-Source Project Customization
A complete methodology for open-source project customization based on real-world experience, detailing the Cursor+Codex dual-IDE workflow, seven-stage process, MVP validation, and AI source code reading techniques.