#!/bin/bash # # foks-kv-download # John Simpson 2025-07-26 # Last updated 2025-07-27 # # Download the contents of a FOKS KV store to a local directory. This is # mainly meant as a backup too, until/unless FOKS has something like KBFS. # # This script works with foks v0.1.1 # ############################################################################### # # Copyright (C) 2025 John Simpson # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the “Software”), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # ############################################################################### ############################################################################### # # Coloured line functions function redline { printf "\e[0;1;37;41m%s\e[0K\e[0m\n" "$*" } function yellowline { printf "\e[0;30;43m%s\e[K\e[0m\n" "$*" } function greenline { printf "\e[0;1;37;42m%s\e[0K\e[0m\n" "$*" } function die { redline "$@" exit 1 } ############################################################################### # # Maybe print a command before executing it. # # Note: this runs the command after (maybe) printing it. If you want to copy # this for your own scripts feel free, but be aware that it doesn't handle # shell redirects correctly. If this is an issue, use show_x instead and run # the command "for real" after calling it. SET_X="${SET_X:-false}" function set_x { if [[ "${SET_X:-false}" == "true" ]] then local IFS=$' ' greenline "$PS4$*" 1>&2 fi "$@" } ######################################## # Show the same output that set_x would show, # but don't actually run the command function show_x { if [[ "${SET_X:-false}" == "true" ]] then local IFS=$' ' greenline "$PS4$*" 1>&2 fi } ############################################################################### # # Maybe print a debugging message function debug { if $DEBUG then local CALLER=$( caller | awk '{ print $1 }' ) yellowline "[$CALLER] $@" fi } ############################################################################### # # Usage message function usage { local MSG="${*:-}" cat <&1 ) ######################################## # If there was NO output, the command succeeded and we're finished # with this key. if [[ ${#LINES[@]} == 0 ]] then continue fi ######################################## # If the output was a real error (i.e. something other than # "this is not a file"), skip it LINE="${LINES[0]}" if [[ -n "$LINE" ]] then RE_NEEDFILE="^Error: needed file, got something else" if [[ ! $LINE =~ $RE_NEEDFILE ]] then echo "unrecognized line '$LINE', ignoring this key" continue fi fi fi fi ############################################################ # Downloading it as a file didn't work, so we know that it's (probably) # a directory in the KV store. We need to create a directory under the # output directory with the same name. # # If the directory we need to create already exists, we need to delete # it first. This happens if the 'foks kv get' command created an empty # file before realizing that the key wasn't a "file" in the KV store. if [[ ! -d "$OFILE" ]] then if [[ -e "$OFILE" ]] then set_x rm -f "$OFILE" || exit 1 fi set_x mkdir "$OFILE" || exit 1 fi ############################################################ # Try to list the key as a directory. The output of this command should # contain a list of keys, all within the directory. If so, add them to # the list of keys to be checked. ######################################## # Build and maybe show the command we'll be running CMD=( foks kv ls "${OPT_LS[@]}" "$KEY" ) show_x "${CMD[@]}" ######################################## # Run the command and gather the lines of its output into an array LINES=() while IFS=$'' read -r LINE do LINES+=( "$LINE" ) done < <( "${CMD[@]}" 2>&1 ) ######################################## # Check each line of output. If it starts with the root key we're # downloading, add it to the list of keys to be processed. Otherwise # show it on the screen, it's probably an error message. ERR_CMD_SHOW=true for LINE in "${LINES[@]}" do debug "$LINE" if [[ "$LINE" =~ ^$ROOT_KEY ]] then debug "- adding" KEYS+=( "$LINE" ) else if $ERR_CMD_SHOW then if ! $SET_X then ZZZ="$IFS" IFS=$' ' yellowline "$PS4$*" 1>&2 IFS="$ZZZ" fi ERR_CMD_SHOW=false fi echo "$LINE" fi done done