5 Lightweight Bash HTML Editor Scripts to Try Today

How to Build a Simple Bash HTML Editor in MinutesCreating a simple HTML editor using Bash is a great way to learn shell scripting, text processing, and basic HTML structure. This guide walks you through building a minimal, usable command-line HTML editor that supports creating, opening, editing specific HTML elements, previewing in a browser, and saving changes. No external GUI libraries required — just Bash, common Unix utilities, and a web browser.


Why build a Bash HTML editor?

  • Fast setup: Bash scripts run on most Unix-like systems without installing extra packages.
  • Educational: You learn shell scripting, file handling, and HTML structure.
  • Lightweight: Perfect for quick edits on remote servers or lightweight development environments.

What this editor will do

  • Create a new HTML file from a template.
  • Open an existing HTML file.
  • List editable elements (title, headings, paragraphs, links) and let the user pick one to edit.
  • Insert new elements.
  • Preview the HTML file in the default web browser.
  • Save and quit.

Prerequisites

  • Bash (v4+ recommended for associative arrays).
  • Standard Unix utilities: sed, awk, grep, sleep, printf, mktemp, xdg-open (Linux) or open (macOS).
  • A terminal text editor (nano, vi) is optional but not required.

Script overview

The editor script will:

  1. Provide a menu-driven interface.
  2. Use basic parsing with grep/sed to find elements (title, h1–h6, p, a).
  3. Allow editing an element inline or open it in $EDITOR.
  4. Save changes back to the file safely (write to temp file, then move).
  5. Offer a preview option that opens the file in the system default browser.

The script

Copy this into a file named bash-html-editor.sh and make it executable (chmod +x bash-html-editor.sh).

#!/usr/bin/env bash # Simple Bash HTML Editor set -euo pipefail FILE="${1:-}" EDITOR_CMD="${EDITOR:-nano}" TMP="$(mktemp --tmpdir bash_html_edit.XXXXXX.html)" cleanup() {   rm -f "$TMP" } trap cleanup EXIT usage() {   cat <<EOF Usage: $0 [file.html] If no file is provided, you'll be prompted to create one. EOF   exit 1 } new_template() {   cat > "$1" <<'HTML' <!doctype html> <html lang="en"> <head>   <meta charset="utf-8">   <title>Untitled</title> </head> <body>   <h1>Welcome</h1>   <p>Edit this paragraph.</p> </body> </html> HTML } ensure_file() {   if [[ -z "$FILE" ]]; then     read -rp "Create new file name (e.g. index.html): " FILE   fi   if [[ ! -e "$FILE" ]]; then     new_template "$FILE"     echo "Created $FILE from template."   fi } list_elements() {   # Print elements with numeric index   awk '     BEGIN{idx=0}     /<title[^>]*>/ { gsub(/.*<title[^>]*>|</title>.*/, ""); print ++idx ": title: " $0; next }     /<h[1-6][^>]*>/ { match($0, /<h[1-6][^>]*>(.*)</h[1-6]>/, m); if(m[1]!="") print ++idx ": heading: " m[1]; next }     /<p[^>]*>/ { match($0, /<p[^>]*>(.*)</p>/, m); if(m[1]!="") print ++idx ": paragraph: " m[1]; next }     /<a [^>]*>/ { match($0, /<a[^>]*>(.*)</a>/, m); if(m[1]!="") print ++idx ": link: " m[1]; next }   ' "$FILE" } element_locations() {   # Create a list of line numbers and simple ids for editable elements   awk '     BEGIN{idx=0}     {       line=$0       if(match(line, /<title[^>]*>/)) {         if(match(line, /<title[^>]*>(.*)</title>/, m)) {           print ++idx ":title:" NR ":" m[1]         }       } else if(match(line, /<h[1-6][^>]*>/)) {         if(match(line, /<(h[1-6])[^>]*>(.*)</>/, m)) {           print ++idx ":heading:" NR ":" m[2]         }       } else if(match(line, /<p[^>]*>/)) {         if(match(line, /<p[^>]*>(.*)</p>/, m)) {           print ++idx ":paragraph:" NR ":" m[1]         }       } else if(match(line, /<a [^>]*>/)) {         if(match(line, /<a[^>]*>(.*)</a>/, m)) {           print ++idx ":link:" NR ":" m[1]         }       }     }   ' "$FILE" } edit_line_inline() {   local lineno="$1"   local before after current new   before="$(sed -n "$lineno p" "$FILE")"   echo "Current: $before"   read -rp "New content (enter the inner HTML to replace): " new   # Replace inner HTML between tags   # Use perl for safer regex multi-char handling   perl -0777 -pe "     s{(<[^>]+>)[^<]*(</[^>]+>)}{$1${new//\/\\}${2}} if $. == $lineno;   " "$FILE" > "$TMP" && mv "$TMP" "$FILE"   echo "Updated line $lineno." } edit_with_editor() {   cp "$FILE" "$TMP"   "$EDITOR_CMD" "$TMP"   mv "$TMP" "$FILE"   echo "Saved changes from $EDITOR_CMD." } insert_element() {   echo "Choose element to insert:"   select el in "h1" "h2" "h3" "p" "a" "Cancel"; do     case $el in       h1|h2|h3|p)         read -rp "Enter text: " txt         sed -n '$p' "$FILE" >/dev/null 2>&1         # Insert before closing body tag         awk -v tag="$el" -v txt="$txt" '{           if(tolower($0) ~ /</body>/ && !inserted) {             print "  <"tag">"txt"</"tag">"             inserted=1           }           print         }' "$FILE" > "$TMP" && mv "$TMP" "$FILE"         echo "Inserted <$el>."         break         ;;       a)         read -rp "Enter href: " href         read -rp "Enter link text: " txt         awk -v href="$href" -v txt="$txt" '{           if(tolower($0) ~ /</body>/ && !inserted) {             print "  <a href=""href"">"txt"</a>"             inserted=1           }           print         }' "$FILE" > "$TMP" && mv "$TMP" "$FILE"         echo "Inserted <a>."         break         ;;       Cancel) break ;;     esac   done } preview() {   if command -v xdg-open >/dev/null 2>&1; then     xdg-open "$FILE" >/dev/null 2>&1 &   elif command -v open >/dev/null 2>&1; then     open "$FILE" >/dev/null 2>&1 &   else     echo "No browser opener found. File is at $FILE"   fi } main_menu() {   while true; do     echo     echo "File: $FILE"     echo "1) List editable elements"     echo "2) Edit an element inline"     echo "3) Edit full file in $EDITOR ($EDITOR_CMD)"     echo "4) Insert element"     echo "5) Preview in browser"     echo "6) Save and exit"     echo "7) Exit without saving"     read -rp "Choice: " c     case "$c" in       1)         element_locations || echo "No editable elements found."         ;;       2)         echo "Select element number to edit:"         element_locations         read -rp "Element #: " n         loc=$(element_locations | awk -F: -v num="$n" '$1==num{print $0}')         if [[ -z "$loc" ]]; then echo "Invalid selection."; continue; fi         lineno=$(echo "$loc" | awk -F: '{print $3}')         edit_line_inline "$lineno"         ;;       3) edit_with_editor ;;       4) insert_element ;;       5) preview ;;       6) echo "Saved to $FILE"; break ;;       7) echo "Aborted. No further changes saved."; exit 0 ;;       *) echo "Invalid choice." ;;     esac   done } # Run ensure_file main_menu 

How it works (brief)

  • element_locations and list_elements use awk to find simple single-line elements. This editor is minimal and best for small static files where elements are on one line.
  • edit_line_inline replaces inner HTML of a specific line using perl to avoid sed’s multi-char regex issues.
  • insert_element appends new elements before .
  • preview uses xdg-open/open to display the file.

Limitations and improvements

  • This script assumes elements are on a single line; it won’t robustly handle complex, multiline HTML.
  • It does not parse attributes beyond basic matching.
  • For robust HTML manipulation consider using a proper parser (Python + BeautifulSoup, Node + jsdom) or an ncurses UI (dialog, whiptail, fzf).
  • To support multiline elements, modify parsing to use an HTML-aware tool or a stateful awk/perl routine.

Next steps / enhancements

  • Add undo/redo by keeping timestamped backups in a .bak folder.
  • Support editing element attributes (class, id, href).
  • Add search-and-replace for quick edits.
  • Convert to a more interactive TUI with dialog or fzf for navigation.

This simple Bash HTML editor is intentionally small and practical for quick edits or learning scripts. It can be expanded incrementally as your needs grow.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *