Practical LLM Applications on the Linux Command Line
Small shell scripts are often sufficient to turn a general-purpose LLM into a repeatable Linux tool.
The examples on this page cover direct local prompting, provider-independent prompting, OCR, plant
recognition and transcript summarization. The deliberately minimal myllmcalllocal talks
directly to the Ollama command line and lets the user choose an installed model interactively. The more
automated scripts move model choice into ~/models.txt and can delegate inference either to
OpenCode with a provider such as OpenRouter, or directly to Ollama. OpenCode Zen and local Ollama models
are natural alternatives without changing the basic workflow design.
The scripts follow the usual Unix division of labour. Bash handles arguments, files, naming, loops,
subprocesses and deterministic text processing. The LLM is called only for the semantic part of the
task: understanding an image, transcribing a scan, identifying a plant or compressing a transcript.
This keeps the surrounding workflow transparent and easy to automate.
Bash script
arguments, files, pipes
model choice→
interactive or ~/models.txt
simple local call or automated configuration
inference→
Ollama or OpenCode
local or provider-backed LLM
Generic LLM calls
myllmcalllocal is the minimal direct Ollama variant for a task file and interactive model
selection. myopencodecall turns opencode run into a provider-independent shell
command for a prompt plus an optional text or image file.
OCR and vision
myollamaocr performs local OCR through Ollama, while myopenrouterocr and
myopenrouterimagerecognition use OpenCode with a vision-capable provider model.
Transcript processing
myyoutubesubs downloads and cleans YouTube subtitles; myyoutubesubs_local
extracts subtitles from local MP4 files. Both can optionally ask an LLM for a condensed version.
1
myllmcalllocal: The Simplest Direct Local LLM Call
A minimal Bash wrapper around the local ollama command.
myllmcalllocal demonstrates the shortest path from a text file to a locally installed language
model. It accepts exactly one task file, verifies that the ollama command exists, obtains the
locally available model names from ollama list, and presents them through Bash's built-in
select menu. The selected model then receives the complete file content on standard input.
The model response is written to result.txt.
Interface
Usage: myllmcalllocal [-h] <task>.txt
The task itself is simply a text file. There is no provider configuration, API key, model option or
OpenCode dependency. The only runtime prerequisite is a working local Ollama installation with at
least one downloaded model.
Design characteristic
Model selection is intentionally interactive rather than configuration-driven. This makes the script
well suited for manual experiments and illustrates the local inference mechanism without an additional
abstraction layer. For unattended or repeatable workflows, the later scripts replace this interaction
with ~/models.txt.
Essential excerpt · discover and select a local model
models=$(ollama list | awk -F' ' '{print $1}')
options=($models)
PS3="Please select a model: "
select model in "${options[@]}"; do
if [[ -n $model ]]; then
break
fi
echo "Invalid option. Please select a valid model."
done
Essential excerpt · invoke Ollama through a Unix pipe
cat "$1" | ollama run "$model" > result.txt
Local workflow
task.txt→myllmcalllocal→ollama list→interactive model choice→ollama run→result.txt
2
Central Model Selection with ~/models.txt
Model choice is configuration, not a command-line option.
Each script derives its own name from $0 and searches ~/models.txt for a line
beginning with that name followed by a colon. If no script-specific entry exists, the line
default: is used. The user can therefore switch models globally or per application without
modifying the scripts and without adding a model-selection option to every command line.
If neither exists, terminate with an explicit error.
For OpenCode calls, model identifiers use OpenCode's provider/model notation. The local
myollamaocr script is the deliberate exception because it talks directly to Ollama and
therefore uses Ollama's own model name such as glm-ocr.
A small wrapper around opencode run for shell scripts and one-shot commands.
myopencodecall is the generic member of the set. The remaining command-line arguments form
the prompt. With -f, one text or image file is attached. The model itself is deliberately not
selectable on the command line; it is resolved through ~/models.txt and then passed to
OpenCode with -m internally.
Essential excerpt · constructing the OpenCode invocation
load_model
prompt="$*"
args=(run -m "$MODEL" "$prompt")
if [ -n "$file" ]; then
args+=(-f "$file")
fi
opencode "${args[@]}"
myopencodecall "Explain the difference between fork and exec."
myopencodecall -f notes.txt "Summarize the attached file in five bullet points."
myopencodecall -f scan.png "Describe this image."
4
OCR: Local Ollama or Cloud Vision Model
Two scripts implement the same application with different inference paths.
myollamaocr · local OCR
This script bypasses OpenCode and calls the local Ollama generate API directly. The image is Base64
encoded, inserted into a JSON request with jq, and sent to
http://127.0.0.1:11434/api/generate. The recognized text is written beside the image as
a .txt file.
Here OpenCode supplies the attachment handling and provider abstraction. The script contributes the
task-specific OCR prompt, the input file and the configured model. With the supplied configuration,
that model is obtained through OpenRouter.
opencode run \
-m "$MODEL" \
'Perform OCR on this scanned document.
Transcribe all visible text accurately,
preserve line and paragraph structure where possible,
and output only the transcription.' \
-f "$file" \
> "$out"
Two OCR paths
image→myollamaocr→Ollama localhost→local vision model
image→myopenrouterocr→OpenCode→OpenRouter / alternative provider
5
myopenrouterimagerecognition: Plant Recognition
Vision models can be wrapped just as easily as text models.
This script asks a vision-capable model to identify a plant from an image. It returns the most likely
German common name and, where reliable, the scientific name. In single-file mode it creates a matching
.txt file. Batch mode scans the current directory for JPEG images and additionally produces
recognition.csv.
Essential excerpt · task-specific vision prompt
opencode run \
-m "$MODEL" \
'Identify the plant shown in this image.
Give the most likely German common name first,
followed by the scientific name in parentheses
if you can determine it reliably.
If the identification is uncertain, say so and
give the most likely alternatives.
Output only the identification.' \
-f "$file" \
> "$out"
Plant-recognition workflow
JPG / JPEG→script or batch loop→vision model→TXT+recognition.csv
The supplied models.txt assigns the vision-capable
openrouter/qwen/qwen3-vl-8b-instruct specifically to this script, independently of the
default text model. This is exactly the kind of specialization for which the per-script configuration
was introduced.
6
Transcript Processing and Optional LLM Summaries
The deterministic part stays in ordinary command-line tools; OpenCode is added only at the end.
myyoutubesubs · online source
yt-dlp downloads automatic subtitles for a single YouTube URL or a URL list. An AWK
stage removes WebVTT headers, timestamps, tags, entities, empty lines and immediately repeated lines.
The result becomes a normal text file. With -s, OpenCode summarizes that file.
yt-dlp \
--write-auto-subs \
--skip-download \
"$URL"
# ... VTT cleanup with awk ...
opencode run -m "$MODEL" -f "$file" \
"Summarize the content of the attached file
concisely and completely. Output only the summary." \
> "$tmp"
fold -s -w 80 "$tmp" > "$condensed"
myyoutubesubs_local · local MP4 source
The local counterpart processes all MP4 files in the current directory. ffprobe locates
an embedded subtitle stream, preferring the stream marked as default. ffmpeg extracts it
as SRT, with WebVTT as fallback. The same cleanup and optional OpenCode summarization then follow.
YouTube / MP4→yt-dlp or ffprobe/ffmpeg→AWK cleanup→TXT→OpenCode summary→*_condensed.txt
7
OpenCode, OpenRouter, OpenCode Zen and Ollama
The scripts separate application logic from the concrete model provider.
OpenRouter
In the supplied setup, most OpenCode-based scripts use model IDs beginning with
openrouter/. OpenCode handles the file attachment and request; OpenRouter acts as the
hosted model gateway. A single OpenCode installation can therefore use different text and vision
models without provider-specific shell code.
OpenCode Zen
OpenCode Zen is an alternative provider integrated into OpenCode. It exposes a curated set of models
tested for OpenCode and is configured like other providers. After authentication, a Zen model can be
selected with an OpenCode model ID such as opencode/<model>. For these scripts, changing
the corresponding entry in ~/models.txt is sufficient; the application code remains the same.
Ollama
Ollama is the local alternative. It can be used directly, as in myollamaocr, or exposed
as an OpenCode provider. In the latter case the existing OpenCode-based scripts can keep their
opencode run workflow while inference occurs on localhost, provided the local model has
the required text or vision capabilities.
Provider abstraction through OpenCode
application script→model ID from ~/models.txt→OpenCode→OpenRouter/OpenCode Zen/Ollama
Why keep OpenCode between Bash and the provider?
The shell scripts need only one stable interface: opencode run -m MODEL PROMPT -f FILE.
Provider authentication, model discovery and provider-specific communication remain OpenCode's job.
This is especially useful for multimodal calls because the Bash code does not need to construct provider-specific
JSON payloads or Base64 attachments. The direct Ollama OCR script demonstrates the opposite design when full local
control and a minimal HTTP dependency are preferred.
Switching provider without changing the script
# Hosted gateway
myopenrouterocr:openrouter/qwen/qwen3-vl-8b-instruct
# Alternative: OpenCode Zen
myopenrouterocr:opencode/<vision-model>
# Alternative: locally configured Ollama provider in OpenCode
myopenrouterocr:ollama/<vision-model>
Direct Comparison of the Seven Applications
Script
Purpose
Deterministic tools
LLM path
Output
myllmcalllocal
simple local task-file execution
Bash, awk, select, pipe
direct Ollama CLI, interactively selected model
result.txt
myopencodecall
generic prompt + optional file
Bash argument handling
OpenCode → configured provider
stdout
myollamaocr
local document OCR
base64, jq, curl
direct Ollama API
basename.txt
myopenrouterocr
cloud/provider document OCR
Bash file handling
OpenCode → OpenRouter by current config
basename.txt
myopenrouterimagerecognition
plant identification
Bash loop + CSV output
OpenCode → vision provider
TXT + optional CSV
myyoutubesubs
YouTube subtitle extraction + summary
yt-dlp, awk, fold
optional OpenCode summary
TXT / condensed TXT
myyoutubesubs_local
embedded MP4 subtitles + summary
ffprobe, ffmpeg, awk, fold
optional OpenCode summary
TXT / condensed TXT
Common pattern:
keep deterministic processing in shell utilities and put only the semantic step behind an LLM call. The
minimal myllmcalllocal variant chooses a local Ollama model interactively; the automated scripts
keep model selection in ~/models.txt. OpenCode provides a uniform bridge to hosted or local
providers, while direct Ollama access remains useful where a completely local, narrowly controlled call is
preferable. The scripts therefore remain small while the model backend can evolve independently.
Official References
OpenCode models and provider configuration: opencode.ai/docs/models and opencode.ai/docs/providers
OpenCode Zen: opencode.ai/docs/zen
OpenRouter provider and model gateway: openrouter.ai