Need help with your JSON?

Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool

Shell Script Wrappers for JSON Formatting Tools

Working with JSON data is a ubiquitous task for developers. Whether you're debugging API responses, parsing configuration files, or processing logs, JSON is everywhere. While many command-line tools exist to help format and process JSON (like jq, json_pp, or python -m json.tool), their commands can sometimes be lengthy, difficult to remember, or inconsistent depending on the specific tool or task.

This is where shell script wrappers come in. A simple wrapper script can abstract away the complexities of these tools, provide a consistent interface, and automate common tasks, making your workflow much more efficient.

Why Wrap JSON Tools?

Wrapping command-line JSON tools in shell scripts offers several benefits:

  • Simplified Commands: Turn complex commands with multiple flags into short, memorable aliases (e.g., fmtjson instead of jq . or python -m json.tool).
  • Consistency: Use the same wrapper command regardless of which underlying tool is available or preferred on a given system.
  • Handling Input: Easily handle input from stdin (piped data), files, or even the system clipboard.
  • Adding Features: Integrate extra steps like colorization, diffing against another file, or extracting specific fields by default.
  • Automation: Combine multiple steps (like fetching data, formatting, and filtering) into a single command.

Basic Wrapper Example: Pretty-Printing

Let's start with a simple wrapper to pretty-print JSON. This script will try to use jq if available, falling back to python -m json.tool or json_pp.

fmtjson script:

#!/bin/bash

# Simple JSON formatter wrapper
# Tries jq, then python -m json.tool, then json_pp

# Check for input from pipe or file
if [ -p /dev/stdin ]; then
  # Input is from pipe (stdin)
  JSON_INPUT=$(cat)
elif [ -n "$1" ] && [ -f "$1" ]; then
  # Input is from a file provided as the first argument
  JSON_INPUT=$(cat "$1")
else
  echo "Usage: $0 [file] < JSON_data" >&2
  echo "       Provide JSON via pipe or file argument." >&2
  exit 1
fi

# Try jq first
if command -v jq &>/dev/null; then
  echo "$JSON_INPUT" | jq .
  exit 0
fi

# If jq not found, try python -m json.tool
if command -v python &>/dev/null; then
  echo "$JSON_INPUT" | python -m json.tool
  exit 0
fi

# If python not found, try json_pp
if command -v json_pp &>/dev/null; then
  echo "$JSON_INPUT" | json_pp
  exit 0
fi

# If none found
echo "Error: No JSON formatting tool found (need jq, python, or json_pp)." >&2
exit 1

Save this as fmtjson in your $PATH and make it executable (chmod +x fmtjson).

Now you can use it like this:

# From a string (using echo and pipe)
echo '{"name": "Alice", "age": 30}' | fmtjson

# From a file
fmtjson config.json

# Example API call piped into fmtjson
curl -s "https://rickandmortyapi.com/api/character/1" | fmtjson

Handling Different Inputs (Files vs. Stdin)

The basic example already handles both piped input and a file argument. Let's refine this pattern. A common technique is to check if stdin is connected to a terminal (`-t 0`) or if there's a file argument.

Input Handling Pattern:

#!/bin/bash

# Determine input source
if [ -t 0 ]; then
  # Stdin is connected to a terminal, expect file argument
  if [ -z "$1" ]; then
    echo "Usage: $0 <json_file> [options]" >&2
    exit 1
  fi
  INPUT_SOURCE="$1"
  # Shift arguments if file is used, so options are $1, $2, etc.
  shift
else
  # Input is from pipe (stdin)
  INPUT_SOURCE="/dev/stdin"
fi

# Now use INPUT_SOURCE with your JSON tool
# Example using jq:
if command -v jq &>/dev/null; then
  jq "$@" "$INPUT_SOURCE"
else
  echo "jq not found!" >&2
  exit 1
fi

In this pattern, $@ passes along any additional arguments provided to the wrapper script (like jq filters).

Usage with this pattern:

# With a file and jq filter
my_jq_wrapper config.json '.users[] | .name'

# With piped input and jq filter
cat log.json | my_jq_wrapper '.errors | length'

Integrating Clipboard (macOS & Linux)

A very common use case is formatting JSON that you've copied to your clipboard. You can extend your wrapper to read from and write to the clipboard using OS-specific commands like pbcopy/pbpaste (macOS) or xclip/xsel (Linux).

Wrapper with Clipboard Support:

#!/bin/bash

# JSON formatter with clipboard support
# Usage: fmtjson [file]   -> Format file content
#        ... | fmtjson    -> Format piped content
#        fmtjson -p       -> Format content from pasteboard (clipboard)
#        fmtjson -p | pbcopy # or xclip -selection clipboard -> Format clipboard and copy back

INPUT_SOURCE="/dev/stdin" # Default to stdin
USE_CLIPBOARD=false

# Check for pasteboard flag (-p)
if [ "$1" == "-p" ]; then
  USE_CLIPBOARD=true
  shift # Remove -p from arguments
fi

# Determine input source (file or stdin) if not using clipboard
if ! $USE_CLIPBOARD; then
  if [ -t 0 ]; then
    # Stdin is terminal, expect file argument
    if [ -z "$1" ]; then
      echo "Usage: $0 [-p] <json_file> [options]" >&2
      echo "       ... | $0 [options]" >&2
      echo "       $0 -p [options]" >&2
      exit 1
    fi
    INPUT_SOURCE="$1"
    shift # Remove file from arguments
  fi
fi

# Get the JSON input
if $USE_CLIPBOARD; then
  # Read from clipboard
  if command -v pbpaste &>/dev/null; then
    JSON_INPUT=$(pbpaste)
  elif command -v xclip &>/dev/null; then
    JSON_INPUT=$(xclip -selection clipboard -o)
  elif command -v xsel &>/dev/null; then
    JSON_INPUT=$(xsel -b -o)
  else
    echo "Error: Clipboard paste tool not found (need pbpaste, xclip, or xsel)." >&2
    exit 1
  fi
elif [ "$INPUT_SOURCE" = "/dev/stdin" ]; then
  # Read from stdin (pipe)
  JSON_INPUT=$(cat)
else
  # Read from file
  JSON_INPUT=$(cat "$INPUT_SOURCE")
fi

# --- Formatting Logic (using jq as example) ---
# Use jq on the JSON_INPUT variable, passing remaining arguments
if command -v jq &>/dev/null; then
  echo "$JSON_INPUT" | jq "$@"
else
  echo "Error: jq not found. Cannot format JSON." >&2
  exit 1
fi

This script adds a -p flag to read from the clipboard. The output is printed to stdout, so you can pipe it back to the clipboard tool to format in place:

# Format JSON currently in clipboard (macOS)
fmtjson -p | pbcopy

# Format JSON currently in clipboard (Linux using xclip)
fmtjson -p | xclip -selection clipboard

Handling Files with Specific Extensions

You might want your wrapper to behave differently or use different tools based on the file extension, though for pure JSON this is less common than for other formats like YAML or XML. However, you could extend the idea to process .jsonl (JSON Lines) differently than standard .json.

More Advanced Concepts

Error Handling:

Robust wrappers should include error checking, like ensuring the input is valid JSON before attempting to process it (though the tools themselves usually handle this). You can add checks for the presence of required commands (like command -v jq as shown) and provide informative error messages to the user via >&2 (standard error).

Configuration:

Allow users to configure preferred tools or default options using environment variables. For example, JSON_FORMATTER="python" could tell the script to use the Python tool first.

Parameterization:

Design your wrapper to accept arguments that are passed directly to the underlying tool, as shown in the input handling example passing $@ to jq. This maintains the power of the tool while providing the wrapper's convenience.

Conclusion

Shell script wrappers are a powerful way to customize and simplify your command-line workflow for common tasks like JSON formatting. By abstracting tool specifics and handling different input methods, you can create convenient, consistent, and efficient commands tailored to your needs. Start with a simple pretty-printer and expand your wrapper as you discover more recurring JSON tasks.

Need help with your JSON?

Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool