#!/bin/sh usage () { cat <&2 Usage: ${0##*/} [OPTIONS] FOLDERS Output index of folder hierarchies to stdout. This is used as a small "backup" purpose. In case of data loss, it is still possible to get the full file list thanks to the indexes. Version control and encfs-encrypted folders are skipped. Options: -f: When outputting to a file, overwrite if it exists. -w: Output each index to individual files in current folder. It uses the folder name as basename. EOF } opt_noclobber=true opt_file=false while getopts ":fhw" opt; do case $opt in f) opt_noclobber=false ;; h) usage exit ;; w) opt_file=true ;; \?) usage exit 1 ;; esac done shift $((OPTIND - 1)) if [ $# -eq 0 ]; then usage exit 1 fi ## 'realpath' is required in case argument is ending with '.' or '..'. if ! command -v realpath >/dev/null 2>&1; then echo >&2 "'realpath' not found" exit 1 fi for i ; do [ ! -d "$i" ] && continue [ -e "$i"/.encfs*.xml ] && continue ## We strip "./" from find's listing since we don't need it. We could avoid ## printing it in the first place, but there are several shortcomings: ## - Find over '.*' and '*', is bad practice since if will fail on ## non-existing files or for files beginning with a dash. ## - The 'printf' command in find is for GNU find only. ## 'LC_ALL=C sort' is required to make sure to output is consistent across ## different systems. echo >&2 "$i" ## The two following lines do the same for the same time cost. The former is shorter. # find "$i" -type f | awk -v str="$i" '{l=length(str)+2; print substr($0, l)}' | LC_ALL=C sort > "$OUTPUT" (cd -- "$i" && find . \( -path '*.git*' -o -path '*.svn*' -o -path '*.hg*' \) -prune -o -type f) | sed 's/^.\///' | LC_ALL=C sort | \ if $opt_file; then i="$(realpath -- "$i")" base="${i##*/}" output="$base.index" [ -e "$output" ] && $opt_noclobber && output="$base-$(date +%F-%T).index" echo "$output" cat > "$output" else cat fi done