#!/usr/bin/env bash # # jq_reformat - Reformat JSON files in-place using jq # # Usage: jq_reformat FILE [FILE...] # # This script reformats JSON files in-place by running them through jq. # It preserves the original files by using atomic temporary file replacement, # ensuring data safety even if the process is interrupted. # # Examples: # jq_reformat config.json # jq_reformat *.json # jq_reformat data/*.json settings.json # set -euo pipefail # # Show usage information # usage() { cat <&2 return 1 fi if [[ ! -r "$file" ]]; then echo "Error: File not readable: $file" >&2 return 1 fi # Create a secure temporary file in the same directory as the target # This ensures we're on the same filesystem for atomic mv operation temp_file=$(mktemp "${file}.XXXXXX") || { echo "Error: Failed to create temporary file for: $file" >&2 return 1 } # Set up cleanup trap to remove temp file on error # shellcheck disable=SC2064 trap "rm -f '$temp_file'" EXIT ERR # Run jq to reformat the JSON # - Read from the original file # - Write to temp file # - If successful, atomically replace the original if jq . <"$file" >"$temp_file" 2>/dev/null; then # Preserve original file permissions chmod --reference="$file" "$temp_file" 2>/dev/null || true # Atomically replace the original file mv "$temp_file" "$file" # Clear the trap since we succeeded trap - EXIT ERR return 0 else # jq failed - the file is likely not valid JSON echo "Error: Failed to parse JSON in: $file" >&2 rm -f "$temp_file" trap - EXIT ERR return 1 fi } # # Main script logic # main() { # Handle help flag if [[ $# -eq 0 ]] || [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then usage [[ $# -eq 0 ]] && return 1 return 0 fi local exit_code=0 local file # Process each file argument # Using "$@" properly handles filenames with spaces and special characters for file in "$@"; do if ! reformat_file "$file"; then exit_code=1 # Continue processing other files even if one fails fi done return "$exit_code" } # Run main function with all arguments main "$@"