+get_path_fork_level() {
+ # Desc: Get fork level from two paths
+ # Input: arg1 str path
+ # arg2 str path
+ # Output: stdout int fork level
+ # Version: 0.0.1
+ local path1="$1";
+ local path2="$2";
+
+ # Squeeze multiple slashes and remove trailing slashes
+ path1="$(echo "$path1" | tr -s '/' | sed 's:/*$::' )";
+ path2="$(echo "$path2" | tr -s '/' | sed 's:/*$::' )";
+
+ # Check for mixed absolute/relative paths
+ if [[ "$path1" =~ ^/ ]] && [[ "$path2" =~ ^/ ]]; then
+ flag_root=true;
+ # Remove initial /
+ path1="$(echo "$path1" | sed -e 's:^/::' )";
+ path2="$(echo "$path2" | sed -e 's:^/::' )";
+ elif [[ ! "$path1" =~ ^/ ]] && [[ ! "$path2" =~ ^/ ]]; then
+ flag_root=false;
+ else
+ declare -p path1 path2 flag_root;
+ echo "FATAL:Mixed relative and absolute paths not supported." 1>&2;
+ return 1;
+ fi;
+
+ # Save path as arrays with `/` as element delimiter
+ local IFS='/';
+ read -ra parts1 <<< "$path1";
+ read -ra parts2 <<< "$path2";
+
+ # Get fork level by counting identical path elements from rootside
+ local fork_level=0;
+ for (( i=0; i<${#parts1[@]} && i<${#parts2[@]}; i++ )); do
+ if [[ "${parts1[i]}" != "${parts2[i]}" ]]; then break; fi;
+ ((fork_level++));
+ done;
+
+ echo "$fork_level";
+ #declare -p path1 path2 flag_root parts1 parts2 fork_level; # debug
+ return 0;
+}; # Get fork level int from two paths
+prune_path_rootside() {
+ # Desc: Prunes a path from the root-side to a specified prune level.
+ # Input: arg1 str path
+ # arg2 int prune level (0-indexed)
+ # Depends: GNU sed 4.8
+ # Version: 0.0.1
+ local path="$1";
+ local prune_level="$2";
+
+ # Check for absolute or relative path
+ if [[ "$path" =~ ^/ ]]; then
+ flag_root=true;
+ # Remove initial /
+ path="$(echo "$path" | sed -e 's:^/::' )";
+ else
+ flag_root=false;
+ fi;
+
+ # Save path as array with `/` as element delimiter
+ local IFS='/';
+ read -ra parts <<< "$path";
+
+ # Assemble pruned path from prune_level
+ local pruned_path="";
+ for (( i=prune_level; i<${#parts[@]}; i++ )); do
+ pruned_path+="${parts[i]}/";
+ done;
+
+ # Trim trailing `/` delimiter
+ pruned_path=$(echo "$pruned_path" | sed 's:/*$::');
+
+ # Restore initial / if appropriate
+ if [[ "$flag_root" == "true" ]] && [[ "$prune_level" -eq 0 ]]; then
+ pruned_path=/"$pruned_path";
+ fi;
+
+ # Output pruned path
+ echo "$pruned_path";
+ #declare -p path prune_level parts pruned_path && printf "========\n"; # debug
+ return 0;
+}; # prune path rootside to int specified level
+get_path_hierarchy_level() {
+ # Desc: Outputs hierarchy level of input paths
+ # Example: $ cat lines.txt | get_path_hierarchy_level
+ # Input: stdin str lines with /-delimited paths
+ # Output: stdout int hierarchy level of each path
+ # Version: 0.0.1
+
+ local line level;
+ local flag_root;
+ local -a output;
+
+ n=0;
+ while read -r line; do
+ # Check for mixed absolute/relative paths.
+ if [[ $n -le 0 ]] && [[ "$line" =~ ^/ ]]; then
+ flag_root=true;
+ else
+ flag_root=false;
+ fi;
+ if { [[ "$flag_root" == "true" ]] && [[ ! "$line" =~ ^/ ]]; } || \
+ { [[ "$flag_root" == "false" ]] && [[ "$line" =~ ^/ ]]; } then
+ echo "FATAL:Mixed relative and absolute paths not supported." 1>&2; return 1;
+ fi;
+
+ # Squeeze multiple slashes and remove trailing slashes
+ line="$(echo "$line" | tr -s '/' | sed 's:/*$::' )";
+
+ # Count the number of slashes to determine hierarchy level
+ level="$(echo "$line" | awk -F'/' '{print NF-1}' )";
+ if [[ "$flag_root" == "true" ]]; then ((level--)); fi;
+
+ # Append to output
+ output+=("$level");
+ #declare -p flag_root level; # debug
+ ((n++));
+ done;
+ # Print output
+ printf "%s\n" "${output[@]}";
+}; # return hierarchy level of lines as integers