136 lines
3.2 KiB
Bash
136 lines
3.2 KiB
Bash
|
|
#!/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 <<EOF
|
||
|
|
Usage: ,jq_reformat FILE [FILE...]
|
||
|
|
|
||
|
|
Reformat JSON files in-place using jq.
|
||
|
|
|
||
|
|
This script reformats JSON files by running them through jq's parser,
|
||
|
|
which produces consistently formatted, valid JSON. Files are updated
|
||
|
|
atomically using temporary files to prevent data loss.
|
||
|
|
|
||
|
|
Arguments:
|
||
|
|
FILE One or more JSON files to reformat
|
||
|
|
|
||
|
|
Options:
|
||
|
|
-h, --help Show this help message
|
||
|
|
|
||
|
|
Examples:
|
||
|
|
,jq_reformat config.json
|
||
|
|
,jq_reformat *.json
|
||
|
|
,jq_reformat data/*.json settings.json
|
||
|
|
|
||
|
|
Exit codes:
|
||
|
|
0 Success - all files reformatted
|
||
|
|
1 Error - invalid arguments, missing files, or jq errors
|
||
|
|
EOF
|
||
|
|
}
|
||
|
|
|
||
|
|
#
|
||
|
|
# Reformat a single JSON file in-place
|
||
|
|
#
|
||
|
|
# Arguments:
|
||
|
|
# $1 - Path to the JSON file to reformat
|
||
|
|
#
|
||
|
|
# Returns:
|
||
|
|
# 0 on success, 1 on error
|
||
|
|
#
|
||
|
|
reformat_file() {
|
||
|
|
local file="$1"
|
||
|
|
local temp_file
|
||
|
|
|
||
|
|
# Validate that the file exists and is readable
|
||
|
|
if [[ ! -f "$file" ]]; then
|
||
|
|
echo "Error: File not found: $file" >&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 "$@"
|