CoolFace
Datasetpublic

Frost2o24/bash-instruct-55k

Bash Instruct I — 55,000 verified natural-language → Bash pairs ⚠️ Superseded by Bash Instruct III III doubles the utility vocabulary (89 → 182), adds grouped equivalent answers, real human phrasing from tldr-pages, validation by execution on real Linux, and a style pass that removes shell antipatterns. New work should use III. This version remains useful for one specific purpose: it overlaps III by only ~29%, so it is the one generation that can be deduplicated… See the full description on the dataset page: https://huggingface.co/datasets/Frost2o24/bash-instruct-55k.

sourceHugging Facemitupdated 14d agoView on Hugging Face
1likes64downloads
Dataset Card

Bash Instruct I — 55,000 verified natural-language → Bash pairs

### ⚠️ Superseded by **Bash Instruct III** III doubles the utility vocabulary (89 → 182), adds grouped equivalent answers, real human phrasing from tldr-pages, validation by execution on real Linux, and a style pass that removes shell antipatterns. New work should use III. This version remains useful for one specific purpose: it overlaps III by only ~29%, so it is the one generation that can be deduplicated in as extra volume. See Combining versions.

Bash Instruct I is the first-generation synthetic instruction-tuning dataset in this family, pairing natural-language requests with correct Bash: single commands, short pipelines, and multi-line scripts. It is built for supervised fine-tuning of small LLMs that must turn a plain request into shell code that actually runs.

Every row is a three-turn chat conversation (system / user / assistant) plus two metadata fields (category, utility) for slicing and analysis.

🤗 Dataset viewerhttps://huggingface.co/datasets/Frost2o24/bash-instruct-55k
🐙 Source, generator & validatorhttps://github.com/ya5h-P/bash-instruct-55k
📦 Later generations**III (recommended)** · II
⚖️ LicenseMIT
python
from datasets import load_dataset

ds = load_dataset("Frost2o24/bash-instruct-55k", split="train")

Version comparison

**I** (this)[II](https://huggingface.co/datasets/Frost2o24/bash-instruct-II-55k)[III](https://huggingface.co/datasets/Frost2o24/bash-instruct-III-55k)
Rows55,00054,80354,360
Distinct primary utilities89182182
variant_group (grouped equivalent answers)
Real phrasing seeded from tldr-pages
Validated by execution on real Linux
ShellCheck-clean rate (warning level)97.9%98.5%99.8%
Antipattern style pass (SC2002, SC2010)

This version is validated statically onlybash -n, ShellCheck, and the argument-fidelity gate. It was never executed at scale on real Linux, and it has a narrower utility vocabulary. Prefer III.


At a glance

All figures below are recomputed directly from the shipped `bash_dataset.jsonl`, not carried over from an earlier run.

MetricValue
Rows55,000
single / pipeline / script40% / 35% / 25% (exact)
Distinct primary utilities89
bash -n syntax-valid100% (0 / 55,000 failures)
ShellCheck-clean at warning level97.9% of a 6,000-row random sample
Exact-duplicate (request, command) pairs0
Unique request text98.7%
Unique command text75.6%
Argument fidelity100% (0 request/command noun mismatches)
Max share of any single utility4.0% (hard cap of 2,200 rows per utility)
Most common 4-word request opening1.4% of rows
Mean command length85 chars · 13.8 tokens

Note the trade-off against later versions: request text here is more unique (98.7% vs ~80% of rows) because there are no grouped answer variants, but command text is less unique (75.6% vs 78.7%) because the same command recurs under different phrasings.


Quickstart

Load

python
from datasets import load_dataset

ds = load_dataset("Frost2o24/bash-instruct-55k", split="train")
print(ds)
# Dataset({features: ['messages', 'category', 'utility'], num_rows: 55000})

print(ds[0]["messages"])

Fine-tune (TRL SFTTrainer)

The messages column is already in the OpenAI-style chat format that TRL, Axolotl, Unsloth, and LLaMA-Factory consume directly — no preprocessing or column mapping needed.

python
from datasets import load_dataset
from trl import SFTTrainer, SFTConfig

ds = load_dataset("Frost2o24/bash-instruct-55k", split="train")

trainer = SFTTrainer(
    model="Qwen/Qwen2.5-Coder-1.5B-Instruct",
    train_dataset=ds,                       # `messages` is auto-detected
    args=SFTConfig(
        output_dir="bash-sft",
        max_length=1024,
        num_train_epochs=2,
        per_device_train_batch_size=8,
    ),
)
trainer.train()

Analyze and slice

python
import collections

print(collections.Counter(ds["category"]))
print(collections.Counter(ds["utility"]).most_common(15))

scripts = ds.filter(lambda r: r["category"] == "script")

There is no variant_group in this version, so a plain random row split is safe — every row is a distinct (request, command) pair.

Inspect without Python

bash
head -n 1 bash_dataset.jsonl | jq .
jq -r '.utility'  bash_dataset.jsonl | sort | uniq -c | sort -rn | head -20
jq -r '.category' bash_dataset.jsonl | sort | uniq -c

Dataset structure

One JSON object per line in bash_dataset.jsonl:

json
{
  "messages": [
    {"role": "system",    "content": "You are a Bash expert."},
    {"role": "user",      "content": "Show the last 20 lines of error.log."},
    {"role": "assistant", "content": "tail -n 20 error.log"}
  ],
  "category": "single",
  "utility": "tail"
}
FieldTypeDescription
messageslist[{role, content}]The training conversation, always exactly systemuserassistant.
categorystringsingle · pipeline · script. See below.
utilitystringPrimary command of the solution (grep, awk, find, for, …). Used to enforce the per-command cap, and useful for slicing or re-balancing training.

Verified schema integrity: all 55,000 lines parse as JSON, all carry the same three keys, all have exactly the system/user/assistant role order, and no message content is empty.

Categories

`category`RowsShareWhat it contains
single22,00040%One command, no pipe or chaining. tail -n 20 error.log
pipeline19,25035%Pipes, &&/`\\, $(...), xargs. ps aux \awk '$3>50 {print $2}'`
script13,75025%Multi-line Bash (JSON-escaped \n), mean 9.8 lines.

What's in the scripts

FeatureShare of `script` rows
set -euo pipefail header100%
for loop47.8%
if [ … ] test13.8%
while loop11.9%
getopts argument parsing9.7%
trap cleanup0.1%
Here-docs (<<)0.1%

Scripts are short, safe-by-default operational snippets — not large programs. trap and here-doc usage is deliberately rare, and there are no function definitions.

System & info utility coverage

Small models routinely fumble the observability and process-management tools. These carry enforced minimum counts with correct, idiomatic flag usage:

UtilityRowsUtilityRowsUtilityRows
journalctl2,200du806pstree782
lsof673ss332renice296
nice293vmstat286ps242
netstat239iostat184w158
dmesg156systemctl111free90
df45uptime34hostname25

uptime and hostname are lower because their genuine idiomatic command space is small; they are covered with real flag variants rather than padded with synonymous phrasings.

System prompts

Five equivalent Bash-assistant system prompts are rotated near-uniformly (~11.0k rows each) so the model does not overfit one string.


Quality assurance

1. Syntax — bash -n on every command

0 failures out of 55,000 (100% pass), verified with GNU bash 5.2.37 on Debian.

2. Static analysis — ShellCheck at warning level

On a 6,000-row random sample (ShellCheck 0.10.0): 97.95% of commands are completely clean. 123 rows produced a finding, almost all of them a single style nit:

CodeFindingsWhat it is
SC2010115`ls \grep` instead of a glob — 93% of all findings
SC20463Unquoted command substitution
SC20623Unquoted grep pattern
SC21642cd without `\\exit`

All findings are warning level; there are no ShellCheck errors. The SC2010 pattern is fixed at the generator level in Bash Instruct III.

3. Argument fidelity

Every pair is gated so the command actually references the concrete nouns the request names — the same filename, extension, user, group, service, process, and port. The shipped file has 0 mismatches.

This gate exists because of a real failure in an earlier build of this generator: roughly 12% of pairs had the right command shape but the wrong concrete noun — a different filename than the request asked for. Neither bash -n nor ShellCheck can detect that, because a wrong filename still parses and still lints clean. argcheck.py closes exactly that gap, and it is the most important quality lesson in this dataset family.

Not validated by execution

Unlike II and III, this version was never executed at scale on real Linux. Many commands (systemctl, journalctl, apt, vmstat) are idiomatic but were validated statically only. The whole-set execution pass applied to II found 43 commands that were malformed in ways static analysis missed; comparable defects are likely present here and unremoved.

Reproduce the audit yourself

bash
# argument-fidelity audit over the shipped file
python argcheck.py --data bash_dataset.jsonl

# re-run bash -n over the whole file
python generate.py validate-all

The standalone validate.py — including the sandboxed execution modes — ships with II and III and can be pointed at this file.


Provenance

How it is built

Generated by a recipe engine (generate.py, included and reproducible). Each recipe family emits (request, command) pairs by combining hand-written phrasing templates (imperative / question / casual) with realistic parameter pools: plausible filenames, directories, ports, services, users, patterns.

Every parameter is drawn once per example and reused in both the request and the command, so the two can never disagree.

The generator enforces:

  • category quotas (40 / 35 / 25),
  • a 4% per-utility cap (2,200 rows) so no command dominates — a real failure mode of earlier runs,
  • minimum coverage floors for the system/info utilities above,
  • SHA-1 deduplication of (request, command) pairs,
  • an argument-fidelity gate (argcheck.py) — the command must reference the same concrete nouns the request names, or the pair is rejected,
  • phrasing diversification so no single opening template dominates,
  • a bash -n syntax gate on every command before it is written.

Regenerating from source

bash
python generate.py plan            # quota table + registered recipes
python generate.py run             # generate to ./bash_dataset.jsonl (resumable)
python generate.py status          # where a run stands
python generate.py validate-all    # re-run bash -n over the whole file

bash_dataset.jsonl is ground truth and a derived progress.json snapshot is rewritten after every batch, so an interrupted run continues where it stopped.


Files

FileDescription
bash_dataset.jsonlThe dataset — 55,000 rows, ~20 MB (no Git LFS needed).
generate.pyThe generator. Reproducible and resumable.
argcheck.pyArgument-fidelity checker — generation gate and standalone auditor.

generate.py and argcheck.py are pure Python 3 with no third-party dependencies. Loading the dataset requires only datasets (or nothing at all — it is plain JSONL).


Combining versions

Overlap of (request, command) pairs, measured across the shipped files:

PairShared pairsShare of the smaller set
I ∩ III15,89629.2% — mostly distinct, safe to merge
I ∩ II18,20033.2%
II ∩ III50,71592.5% — never concatenate those two

This is version I's main remaining use. Because it overlaps III by only ~29%, deduplicating the two together yields roughly 93.5k unique pairs — meaningfully more volume than either alone. Weight it below III, which is measurably higher quality.

python
from datasets import load_dataset, concatenate_datasets

a = load_dataset("Frost2o24/bash-instruct-III-55k", split="train")
b = load_dataset("Frost2o24/bash-instruct-55k", split="train")
b = b.add_column("variant_group", [f"v1-{i}" for i in range(len(b))])

merged, seen = concatenate_datasets([a, b]), set()
merged = merged.filter(
    lambda r: not ((k := (r["messages"][1]["content"], r["messages"][2]["content"])) in seen
                   or seen.add(k))
)
print(len(merged))   # ~93.5k unique pairs

Intended uses & limitations

Intended uses

Teaching a small model to map natural-language requests to correct single Bash commands, short pipelines, and small scripts — including the system/info utilities listed above. In this dataset family its best role is as supplementary volume deduplicated alongside III, rather than as a primary training set.

Limitations

Please read these before reporting results.

  • Superseded. III is better on every measured axis. Use I for extra volume, not as your primary corpus.
  • Not executed. Static validation only; comparable to the 43 malformed commands that whole-set execution later found in II. Some are likely still present here.
  • Synthetic. Variety comes from recombining templates and token pools, not from human authorship. Phrasing is diversified — 98.7% of request strings are unique and no 4-word opening exceeds 1.4% of rows — but the underlying request shapes come from a finite set of families, and ~24% of commands recur under different phrasings.
  • Valid ≠ semantically correct. bash -n, ShellCheck, and the argument gate prove that commands parse, lint, and use the nouns the request named. None of them prove the command's logic satisfies the intent. Some pairs are plausible-but-approximate — an awk column index, for instance, assumes a particular log layout. There is no human correctness audit.
  • Single-turn, one canonical answer. No explanations, alternatives, negative examples, reasoning traces, or multi-turn dialogue.
  • GNU/Linux-centric. Commands assume GNU coreutils and a systemd-based distribution. BSD/macOS flag differences (sed -i, stat, ps) are not covered, and neither are zsh/fish.
  • Short scripts only. Mean 9.8 lines, no function definitions, minimal trap and here-doc coverage. Not a source for large structured shell programs.
  • Not safety-tuned. The corpus contains destructive-capable commands (rm, chmod, chown, kill, dd) as legitimate answers to requests that ask for them. A model trained on it will emit such commands readily. Add refusal/confirmation behaviour separately if your deployment needs it, and never auto-execute model output.

Citation

If you use this dataset, please cite it:

bibtex
@misc{pimplapure2026bashinstruct1,
  title        = {Bash Instruct: A Synthetic Instruction-Tuning Dataset for
                  Natural Language to Bash Generation},
  author       = {Pimplapure, Yash},
  year         = {2026},
  publisher    = {Hugging Face},
  howpublished = {\url{https://huggingface.co/datasets/Frost2o24/bash-instruct-55k}},
  note         = {55,000 chat-formatted examples; 100\% \texttt{bash -n} valid,
                  97.9\% ShellCheck-clean. Superseded by Bash Instruct III}
}

License

MIT.


Supporting this work

This dataset — generation, whole-set Linux execution, and the fine-tuning runs used to check that it actually teaches the task — was built and validated on modest consumer hardware, which is the main thing limiting how far the next version can go.

If your team has an NVIDIA DGX Spark or an AMD Ryzen AI Max+ ("Strix Halo") AI dev kit to spare, it would go directly into larger validated corpora, real fine-tuned baselines, and published eval numbers for this dataset family. Reach out via the dataset discussions tab. No obligation either way — the data stays MIT and freely available regardless.