fslist (3214B)
1 #!/bin/zsh 2 # there is no posix stat(1) and the implementations are riddled with locale-speciffic stuff 3 # better use sane version from zsh 4 5 setopt extendedglob globdots 6 zmodload zsh/stat 7 8 typeset -A ftypes hardlinks s 9 ftypes=( # convert hex type to mnemonic character 10 C s # socket 11 c s 12 A l # symbolic link 13 a l 14 8 f # regular file 15 6 b # block device 16 4 d # directory 17 2 u # character device 18 1 p # FIFO 19 ) 20 21 delim='' 22 23 statement() { 24 # start on new line for multiline statements - more readable 25 if [[ -n delim && $1 == *$'\n'* ]]; then 26 delim=$'\n' 27 fi 28 printf '%s%s' $delim ${1//$'\n'/$'\n\t'} 29 delim=${2:-$'\t'} 30 } 31 32 statement_end() { 33 printf '\n' 34 delim='' 35 } 36 37 ROOT=${${${ROOT:-$PWD}:A}%/} 38 : ${compact:=1} 39 : ${print_m:=1} 40 : ${print_o:=1} 41 : ${print_c:=1} 42 if (($+commands[file])) && (($+commands[base64])); then 43 : ${print_b:=1} 44 else 45 : ${print_b:=0} 46 if (($+commands[file])) && (($+commands[xxd])); then 47 : ${print_x:=1} 48 fi 49 fi 50 : ${print_x:=0} 51 : ${max_bin_size:=} 52 53 fnames=( ) 54 for arg in "${@:-$ROOT}"; do 55 fnames+=( $arg:a ) 56 [[ -d $arg ]] && fnames+=( $arg:a/**/* ) 57 done 58 59 for fname in $fnames; do 60 if ! [[ $fname == $ROOT || $fname == $ROOT/* ]]; then 61 printf "skipping out-of-root file: %s\n" "$fname" >&2 62 continue 63 fi 64 relname=${${fname#$ROOT}#/} 65 zstat -LH s $fname || continue 66 ftype=$(( [##16] $s[mode] >> 12 )) 67 fmode=$(( [##8] $s[mode] & 8#7777 )) 68 t=$ftypes[$ftype] 69 70 (($compact)) || printf '\n' 71 72 if [[ $relname == *$'\t'* || $relname == *$'\n'* ]]; then 73 statement $'P\t'$relname $'\t' 74 else 75 if (($compact)); then 76 statement /$relname 77 else 78 statement /$relname $'\n' 79 fi 80 fi 81 82 if [[ $t != d && $s[nlink] -gt 1 ]]; then 83 id=$s[device]:$s[inode] 84 if (($+hardlinks[$id])); then 85 statement $'H\t'$hardlinks[$id] $'\n' 86 continue 87 else 88 hardlinks[$id]=./$relname 89 fi 90 fi 91 92 if [[ $t == [bu] ]]; then 93 statement $t$(( $s[rdev] >> 8 )):$(( $s[rdev] & 255 )) 94 elif [[ $t == l ]]; then 95 statement $'l\t'$s[link] $'\t' 96 elif [[ $t == f ]]; then 97 if (($print_c)); then 98 if (($s[size])) && (($print_b + $print_x)) && \ 99 [[ $(file -bi $fname) != text/* ]]; then 100 if [[ -n $max_bin_size && $s[size] -gt $max_bin_size ]]; then 101 statement s$'\tSHA512:'${"$(sha512sum < $fname)"%% *} 102 elif (($print_x)); then 103 statement X$'\t'"$(xxd $fname)" $'\n' 104 else 105 statement B$'\t'"$(base64 <$fname)" $'\n' 106 fi 107 else 108 IFS= read -r -d '' content <$fname 109 flags='' 110 if [[ $content == *$'\n' ]]; then 111 content=${content%$'\n'} 112 if [[ $content == *$'\n' ]]; then 113 # force appending newline 114 flags+=n 115 fi 116 else 117 flags+=N 118 fi 119 if ! (($compact)) || [[ $content == *$'\t'* || $content == *$'\n'* ]]; then 120 statement C$flags$'\t'$content $'\n' 121 else 122 statement c$flags$'\t'$content 123 fi 124 fi 125 else 126 statement f 127 fi 128 else 129 statement $t 130 fi 131 (($print_o)) && statement o$s[uid]:$s[gid] 132 (($print_m)) && statement m$fmode 133 statement_end 134 done 135 136 # while getopts omcaOMC o 137 # do case "$o" in 138 # ([?]) 139 # print >&2 "Usage: $(basename "$0") [-omcaOMC] [dir [...]]" 140 # exit 1;; 141 # esac 142 # done 143 144 # find "${@:-$PWD}" -exec stat -c '%F %U:%G %a %h:%i:%d %t:%T %n' '{}' + | awk ' 145 # BEGIN { 146 # FS="\t" 147 # } 148 149 # /\t/ 150 # '