fix summary line stripping bug
[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 # ls -lA with enhanced output
18 # octal permissions
19 # omited acl type specifier
20 # better hard link count: number of subdirectories or number of linked files or omitted if 0
21 # better human readable size
22 # more natural date/time format for my American raised eyes
23 # remove first line size summary
24 ll() {
25 local x y perm line binls sizePadding middle tail size \
26 max_hl_digits hardlinks initial_space hardlink_spacing
27 local max_hl_digits=0
28 local -a lines hl
29 binls=$(type -P ls)
30 local first=true
31 local aclchar=false
32 # there's no way to tell if ls uses the acl specifier unless we loop over the data twice
33 # the 11th char is either
34 # . for selinux context
35 # + for any other kind of acl
36 # or blank for no other kind of acl
37 # I don't want to see this generally.
38 while read line; do
39 # if we did want the first line, it would need to be stripped of non-printing chars:
40 # line=${line#$'\E[00m'}
41 # when no argument is given to ls, the first line is like "total 123M". strip that
42 if $first && [[ ! $@ ]]; then
43 first=false
44 else
45 lines+=("$line")
46 [[ ! ${line:10:1} == " " ]] && aclchar=true
47
48 # we also need to parse the hardlinks on the first pass, because for
49 # example ls could see the highest count as 11, and thus use 3
50 # places for hardlinks, " 10", but then we use 9 or 8 for a more
51 # useful count, and would then use 2 places. So we have to look
52 # through them all because we can't rely on the spacing that ls
53 # decided on.
54 y="${line:11}"
55 initial_space="${y%%[![:space:]]*}"
56 hardlinks="${y#$initial_space}" # remove any initial spaces
57 hardlinks="${hardlinks%%[[:space:]]*}" # remove everything beyond first word
58 # ignore the hardlinks that files/dirs always have
59 if [[ ${line:0:1} == d ]]; then
60 hardlinks=$(( hardlinks - 2 ))
61 else
62 hardlinks=$(( hardlinks - 1 ))
63 fi
64 [[ $hardlinks == 0 ]] && hardlinks=
65 if (( ${#hardlinks} > max_hl_digits )); then
66 max_hl_digits=${#hardlinks}
67 fi
68
69 hl+=("$hardlinks")
70
71 fi
72 done< <( "$binls" -lAh --color=always "--time-style=+%m-%d %Y
73 %m-%d %I:%M %P" "$@" )
74
75 hardlink_spacing=$((max_hl_digits + 1))
76
77 for index in "${!lines[@]}"; do
78 line=${lines[index]}
79 hardlinks=${hl[index]}
80 if ! [[ $line == [-dscbl][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xtT]* ]]; then
81 printf "%s\n" "$line"
82 else
83 perm=0
84 for (( x=0; x<=8; x++ )); do
85 y=${line:$(( -1*x + 9 )):1}
86 [[ $y == [tT] ]] && perm=$(( perm + 512 ))
87 if [[ $y == [sS] ]]; then
88 [[ $x == 3 ]] && perm=$(( perm + 1024 ))
89 [[ $x == 6 ]] && perm=$(( perm + 2048 ))
90 fi
91 [[ $y != [-ST] ]] && perm=$(( perm + 2**x ))
92 done
93 if $aclchar; then
94 y="${line:11}"
95 else
96 y="${line:10}"
97 fi
98 middle=${y#*[^ ]* }
99 size=${middle#*[^ ]* *[^ ]* }
100 middle=${middle%"$size"}
101 tail=${size#*[^ ]* }
102 size=${size%"$tail"}
103 declare -i sizePadding="${#size} - 1"
104 size=( $size ) # remove spaces
105 size=${size/.?/}
106
107 printf "%s%4o%${hardlink_spacing}s%s%${sizePadding}s%s\n" \
108 "${line:0:1}" $perm "$hardlinks" " $middle" "$size" " $tail"
109 fi
110 done
111 }
112