c48eceaa1072c58456f7ec9a0a9637ea1c3e2c37
[small-misc-bash] / ll-function
1 #!/bin/bash
2 # Copyright (C) 2014 Ian Kelling
3
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7
8 # http://www.apache.org/licenses/LICENSE-2.0
9
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16
17 ll() {
18 case $1 in
19 -h|--help)
20 cat <<'EOF'
21 usage: ll [ARGS_TO_LS]
22 ls -lA with enhanced output
23
24 * octal permissions
25 * omited acl type specifier
26 * better hard link count: number of subdirectories or the
27 number of linked files or omitted if it is 0
28 * better human readable size
29 * more natural date/time format for my American raised eyes
30 * remove first line size summary
31
32 EOF
33 ;;
34 esac
35 local x y perm line binls sizePadding middle tail size \
36 max_hl_digits hardlinks initial_space hardlink_spacing
37 local max_hl_digits=0
38 local -a lines hl
39 binls=$(type -P ls)
40 binlslink=$(readlink -f $binls)
41 if [[ $binlslink == */busybox ]]; then
42 $binls -lA "$@"
43 return
44 fi
45 local aclchar=false
46 # there's no way to tell if ls uses the acl specifier unless we loop over the data twice
47 # the 11th char is either
48 # . for selinux context
49 # + for any other kind of acl
50 # or blank for no other kind of acl
51 # I don't want to see this generally.
52 while read -r line; do
53 # if we did want the first line, it would need to be stripped of non-printing chars:
54 #line=${line#$'\E[00m'}
55 # lines like "total 123M", we don't want
56 if [[ ! $line =~ ^total\ [0-9][^\ ]*$ ]]; then
57 lines+=("$line")
58 if ! [[ $line == [-dscbl][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xtT]* ]]; then
59 hardlinks=
60 else
61 [[ ! ${line:10:1} == " " ]] && aclchar=true
62
63 # we also need to parse the hardlinks on the first pass, because for
64 # example ls could see the highest count as 11, and thus use 3
65 # places for hardlinks, " 10", but then we use 9 or 8 for a more
66 # useful count, and would then use 2 places. So we have to look
67 # through them all because we can't rely on the spacing that ls
68 # decided on.
69 y="${line:11}"
70 initial_space="${y%%[![:space:]]*}"
71 hardlinks="${y#$initial_space}" # remove any initial spaces
72 hardlinks="${hardlinks%%[[:space:]]*}" # remove everything beyond first word
73 # ignore the hardlinks that files/dirs always have
74 hardlinks=$(( hardlinks - 1 ))
75 [[ $hardlinks == 0 ]] && hardlinks=
76 if (( ${#hardlinks} > max_hl_digits )); then
77 max_hl_digits=${#hardlinks}
78 fi
79 fi
80 hl+=("$hardlinks")
81 fi
82 done< <( "$binls" -lAh --color=always "--time-style=+%m-%d %Y
83 %m-%d %I:%M %P" "$@" )
84
85 hardlink_spacing=$((max_hl_digits + 1))
86
87 for index in "${!lines[@]}"; do
88 line=${lines[index]}
89 hardlinks=${hl[index]}
90 if [[ $line != [-a-zA-Z][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xtT]* ]]; then
91 # line we don't understand
92 printf "%s\n" "$line"
93 continue
94 fi
95 perm=0
96 for (( x=0; x<=8; x++ )); do
97 y=${line:$(( -1*x + 9 )):1}
98 [[ $y == [tT] ]] && perm=$(( perm + 512 ))
99 if [[ $y == [sS] ]]; then
100 [[ $x == 3 ]] && perm=$(( perm + 1024 ))
101 [[ $x == 6 ]] && perm=$(( perm + 2048 ))
102 fi
103 [[ $y != [-ST] ]] && perm=$(( perm + 2**x ))
104 done
105 if $aclchar; then
106 y="${line:11}"
107 else
108 y="${line:10}"
109 fi
110 middle=${y#*[^ ]* }
111 size=${middle#*[^ ]* *[^ ]* }
112 middle=${middle%"$size"}
113 tail=${size#*[^ ]* }
114 size=${size%"$tail"}
115 declare -i sizePadding="${#size} - 1"
116 size=( $size ) # remove spaces
117 size=${size/.?/}
118
119 printf "%s%4o%${hardlink_spacing}s%s%${sizePadding}s%s\n" \
120 "${line:0:1}" $perm "$hardlinks" " $middle" "$size" " $tail"
121 done
122 }