Skip to content

Instantly share code, notes, and snippets.

@Zash
Last active March 29, 2024 13:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zash/6c2b621a4d889c4e92e16d0efe66554a to your computer and use it in GitHub Desktop.
Save Zash/6c2b621a4d889c4e92e16d0efe66554a to your computer and use it in GitHub Desktop.
In-place editing of json/yaml
#!/bin/bash
# Yaml EDIT
# Uses https://github.com/mikefarah/yq/
set -euo pipefail
declare file=""
declare editformat="yaml"
declare origformat="yaml"
declare editsuffix=""
declare path="." # path to what to edit
declare load="load" # future plain text support
declare gron="false"
declare -i tabs=2
# Time to use getopt?
case $# in
1)
# edit a json (or anything yq supports) file as yaml
# yedit file.json
file="$1";
origformat="${file##*.}"
;;
2)
# edit a subset of a file
# yedit .path file.yaml
path="$1"
file="$2"
origformat="${file##*.}"
;;
3)
# when the file ending does not match the format
# yedit .path yaml actually-yaml.foo
path="$1"
origformat="$2"
file="$3"
;;
4)
# specify the format you want to edit in
# yedit .path json yaml actually-yaml.foo
path="$1"
editformat="$2"
origformat="$3"
file="$4"
;;
0|*)
echo "$0 '.path.to.something' ./file.{json,yaml}" >&2
exit 1
;;
esac
if [[ "$path" == "!" ]]; then
if ! command -v fzf >/dev/null 2>/dev/null ; then
echo fzf missing >&2
exit 1
fi
path="$(yq4 -ot '.. | path | "."+join(".")' "$file" | fzf --reverse --height 20%)"
fi
if [[ "$editformat" == "$origformat" ]] && [[ "$path" == "." ]]; then
# edit whole file in the same format, just invoke editor
exec sensible-editor "$file"
fi
if [[ "$editformat" == "text" ]]; then
editformat="yaml"
editsuffix="md"
load="load_str"
fi
if [[ "$editformat" == "gron" ]]; then
if ! command -v gron >/dev/null 2>/dev/null ; then
echo gron missing >&2
exit 1
fi
gron="true"
editformat="json"
editsuffix="js"
fi
if command -v editorconfig >/dev/null 2>/dev/null ; then
tabs="$(editorconfig "$(readlink -f "$file")" | grep '^indent_size=[0-9]' | cut -d= -f2 || echo 2)"
fi
declare tempfile
tempfile="$(mktemp --suffix=".${editsuffix:-$editformat}")"
origfile="$(mktemp --suffix=".orig")"
cleanup() {
rm -f "$tempfile" "$origfile"
}
trap cleanup EXIT
if ! yq4 --exit-status --no-colors --input-format="$origformat" --output-format="$editformat" --expression="$path" "$file" > "$tempfile"; then
read -n 1 -p "No value, edit anyway? [yn] " -r answer
echo >&2
if [[ "$answer" != y ]]; then
exit 1
fi
fi
if [[ "$gron" == "true" ]]; then
gron "$tempfile" > "$origfile"
mv "$origfile" "$tempfile"
fi
cp -a "$tempfile" "$origfile"
sensible-editor "$tempfile"
if diff -q >/dev/null "$origfile" "$tempfile"; then
echo 'no change' >&2
exit
fi
if [[ "$gron" == "true" ]]; then
gron -u "$tempfile" > "$origfile"
cp "$origfile" "$tempfile"
fi
if [[ "$load" != "load_str" ]]; then
yq4 --exit-status --inplace --input-format="$editformat" --output-format="yaml" "$tempfile"
fi
export tempfile
yq4 --exit-status --inplace --prettyPrint --indent=$tabs --input-format="$origformat" --output-format="$origformat" --expression "$path = $load(strenv(tempfile))" "$file"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment