#!/usr/bin/env bash # Desc: Scan file system and update '.hidden' files to hide certain # files based on filenames. # Version: 0.0.3 # Usage: bk-naut-hide [DIRS...] yell() { echo "$0: $*" >&2; } # print script path and all args to stderr die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status try() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails checkapp() { # Desc: If arg is a command, save result in assoc array 'appRollCall' # Usage: checkapp arg1 arg2 arg3 ... # Version: 0.1.1 # Input: global assoc. array 'appRollCall' # Output: adds/updates key(value) to global assoc array 'appRollCall' # Depends: bash 5.0.3 local returnState #===Process Args=== for arg in "$@"; do if command -v "$arg" 1>/dev/null 2>&1; then # Check if arg is a valid command appRollCall[$arg]="true"; if ! [ "$returnState" = "false" ]; then returnState="true"; fi; else appRollCall[$arg]="false"; returnState="false"; fi; done; #===Determine function return code=== if [ "$returnState" = "true" ]; then return 0; else return 1; fi; } # Check that app exists checkfile() { # Desc: If arg is a file path, save result in assoc array 'fileRollCall' # Usage: checkfile arg1 arg2 arg3 ... # Version: 0.1.1 # Input: global assoc. array 'fileRollCall' # Output: adds/updates key(value) to global assoc array 'fileRollCall'; # Output: returns 0 if app found, 1 otherwise # Depends: bash 5.0.3 local returnState #===Process Args=== for arg in "$@"; do if [ -f "$arg" ]; then fileRollCall["$arg"]="true"; if ! [ "$returnState" = "false" ]; then returnState="true"; fi; else fileRollCall["$arg"]="false"; returnState="false"; fi; done; #===Determine function return code=== if [ "$returnState" = "true" ]; then return 0; else return 1; fi; } # Check that file exists checkdir() { # Desc: If arg is a dir path, save result in assoc array 'dirRollCall' # Usage: checkdir arg1 arg2 arg3 ... # Version 0.1.2 # Input: global assoc. array 'dirRollCall' # Output: adds/updates key(value) to global assoc array 'dirRollCall'; # Output: returns 0 if all args are dirs; 1 otherwise # Depends: Bash 5.0.3 local returnState #===Process Args=== for arg in "$@"; do if [ -z "$arg" ]; then dirRollCall["(Unspecified Dirname(s))"]="false"; returnState="false"; elif [ -d "$arg" ]; then dirRollCall["$arg"]="true"; if ! [ "$returnState" = "false" ]; then returnState="true"; fi else dirRollCall["$arg"]="false"; returnState="false"; fi done #===Determine function return code=== if [ "$returnState" = "true" ]; then return 0; else return 1; fi } # Check that dir exists displayMissing() { # Desc: Displays missing apps, files, and dirs # Usage: displayMissing # Version 1.0.0 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall # Output: stderr: messages indicating missing apps, file, or dirs # Output: returns exit code 0 if nothing missing; 1 otherwise # Depends: bash 5, checkAppFileDir() local missingApps value appMissing missingFiles fileMissing local missingDirs dirMissing #==BEGIN Display errors== #===BEGIN Display Missing Apps=== missingApps="Missing apps :"; #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done for key in "${!appRollCall[@]}"; do value="${appRollCall[$key]}"; if [ "$value" = "false" ]; then #echo "DEBUG:Missing apps: $key => $value"; missingApps="$missingApps""$key "; appMissing="true"; fi; done; if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing. echo "$missingApps" 1>&2; fi; unset value; #===END Display Missing Apps=== #===BEGIN Display Missing Files=== missingFiles="Missing files:"; #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done for key in "${!fileRollCall[@]}"; do value="${fileRollCall[$key]}"; if [ "$value" = "false" ]; then #echo "DEBUG:Missing files: $key => $value"; missingFiles="$missingFiles""$key "; fileMissing="true"; fi; done; if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing. echo "$missingFiles" 1>&2; fi; unset value; #===END Display Missing Files=== #===BEGIN Display Missing Directories=== missingDirs="Missing dirs:"; #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done for key in "${!dirRollCall[@]}"; do value="${dirRollCall[$key]}"; if [ "$value" = "false" ]; then #echo "DEBUG:Missing dirs: $key => $value"; missingDirs="$missingDirs""$key "; dirMissing="true"; fi; done; if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing. echo "$missingDirs" 1>&2; fi; unset value; #===END Display Missing Directories=== #==END Display errors== #==BEGIN Determine function return code=== if [ "$appMissing" == "true" ] || [ "$fileMissing" == "true" ] || [ "$dirMissing" == "true" ]; then return 1; else return 0; fi #==END Determine function return code=== } # Display missing apps, files, dirs get_paths_dothide() { # Desc: Recursively search specified ir for '.hidden' files # Usage: get_paths_dothide arg1 # Input: arg1 dir to search # Output: stdout list of paths of '.hidden' files local # Check input if [[ $# -ne 1 ]]; then die "FATAL:Arg count does not equal 1:$#"; fi; if [[ ! -d "$1" ]]; then die "FATAL:Not a dir:$1"; fi; # Search for files named '.hidden' find "$1" -type f -name ".hidden" 2>/dev/null }; # Return list of paths of '.hidden' files main() { local -a dirs_target pat_tohide # check input # check and specify list of dirs to search for arg in "$@"; do if [[ -d $arg ]]; then dirs_target+=("$arg"); else die "FATAL:Not a dir:arg:$arg"; fi; done; #yell "DEBUG:dirs_target:${dirs_target[*]}"; # specify filename regex patterns to mark '.hidden' pat_tohide+=(".ots$"); pat_tohide+=(".ots.bak$"); #yell "DEBUG:pat_tohide:${pat_tohide[*]}"; # generate list of paths of '.hidden' files for dir in "${dirs_target[@]}"; do #yell "DEBUG:generating list of paths of '.hidden' files in dir:$dir"; while read -r file; do ls_dothide_f+=("$file"); done < <(get_paths_dothide "$dir"); done; #yell "DEBUG:ls_dothide_f:${ls_dothide_f[*]}"; #yell "DEBUG:"; # get list of dirs containing the files while read -r line; do #yell "DEBUG:found .hidden file at:$line"; dir="$(dirname "$line")"; ls_dothide_d+=("$dir"); done < <(printf "%s\n" "${ls_dothide_f[@]}") #yell "DEBUG:ls_dothide_d:${ls_dothide_d[*]}"; #yell "DEBUG:"; # for each dir, write list of files to hide in '.hidden' while read -r dir; do #yell "DEBUG:performing actions in dir:$dir"; declare -a ls_tohide_fn; # array for filenames to write in '.hidden' ## act on each file while read -r file; do #yell "DEBUG:considering file:$file"; filename="$(basename "$file")"; ## check if file is actually a file if [[ ! -f $file ]]; then continue; fi; ## check if file should be hidden according to pattern match unset flag_pat_match; while read -r pat; do if [[ $filename =~ $pat ]]; then flag_pat_match="true"; break; # don't consider any more patterns for this file else flag_pat_match="false"; fi; done < <(printf "%s\n" "${pat_tohide[@]}"); ## add file to list to add to '.hidden' if [[ $flag_pat_match == "true" ]]; then #yell "DEBUG:adding to ls_tohide_fn:file:$file": #yell "DEBUG:filename:$filename"; ls_tohide_fn+=("$filename"); fi; #yell "DEBUG:"; ## unset variables defined only for this file unset filename flag_pat_match; done < <(find "$dir" -mindepth 1 -maxdepth 1 -type f 2>/dev/null); #yell "DEBUG:"; ## add file names to $dir/'.hidden' ### remove blank lines and sort and uniq while read -r line; do ls_tohide_fn_sortuniq+=("$line"); done < <(printf "%s\n" "${ls_tohide_fn[@]}" | sed '/^$/d' | sort | uniq); ls_tohide_fn=("${ls_tohide_fn_sortuniq[@]}"); ### write '.hidden' path_hidefile="$dir"/.hidden; #yell "DEBUG:Writing contents of .hidden file at path_hidefile:$path_hidefile"; #yell "DEBUG:ls_tohide_fn:$(printf "%s\n" "${ls_tohide_fn[@]}" )"; printf "%s\n" "${ls_tohide_fn[@]}" > "$path_hidefile" ## unset variables defined only for this dir unset ls_tohide_fn path_hidefile; #yell "DEBUG:"; done < <(printf "%s\n" "${ls_dothide_d[@]}"); }; # main program main "$@";