#!/bin/bash
-# Copyright (C) 2014 Ian Kelling
+# I, Ian Kelling, follow the GNU license recommendations at
+# https://www.gnu.org/licenses/license-recommendations.en.html. They
+# recommend that small programs, < 300 lines, be licensed under the
+# Apache License 2.0. This file contains or is part of one or more small
+# programs. If a small program grows beyond 300 lines, I plan to switch
+# its license to GPL.
+
+# Copyright 2024 Ian Kelling
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# limitations under the License.
-# ls -lA with enhanced output
-# octal permissions
-# omited acl type specifier
-# better hard link count: number of subdirectories or number of linked files or omitted if 0
-# better human readable size
-# more natural date/time format for my American raised eyes
-# remove first line size summary
+
ll() {
- local x y perm line binls sizePadding middle tail size \
- max_hl_digits hardlinks initial_space hardlink_spacing
- local max_hl_digits=0
- local -a lines hl
- binls=$(type -P ls)
- local first=true
- local aclchar=false
- # there's no way to tell if ls uses the acl specifier unless we loop over the data twice
- # the 11th char is either
- # . for selinux context
- # + for any other kind of acl
- # or blank for no other kind of acl
- # I don't want to see this generally.
- while read line; do
- # if we did want the first line, it would need to be stripped of non-printing chars:
- # line=${line#$'\E[00m'}
- # when no argument is given to ls, the first line is like "total 123M". strip that
- if $first && [[ ! $@ ]]; then
- first=false
- else
- lines+=("$line")
- [[ ! ${line:10:1} == " " ]] && aclchar=true
+ case $1 in
+ -h|--help)
+ cat <<'EOF'
+usage: ll [ARGS_TO_LS]
+ls -lA with enhanced output
+
+* octal permissions
+* omited acl type specifier
+* better hard link count: number of subdirectories or the
+ number of linked files or omitted if it is 0
+* better human readable size
+* more natural date/time format for my American raised eyes
+* remove first line size summary
+
+EOF
+ ;;
+ esac
+ local x y perm line binls sizePadding middle tail size \
+ max_hl_digits hardlinks initial_space hardlink_spacing
+ local max_hl_digits=0
+ local -a lines hl
+ binls=$(type -P ls)
+ binlslink=$(readlink -f $binls)
+ if [[ $binlslink == */busybox ]]; then
+ $binls -lA "$@"
+ return
+ fi
+ local aclchar=false
+ # there's no way to tell if ls uses the acl specifier unless we loop over the data twice
+ # the 11th char is either
+ # . for selinux context
+ # + for any other kind of acl
+ # or blank for no other kind of acl
+ # I don't want to see this generally.
+ while read -r line; do
+ # if we did want the first line, it would need to be stripped of non-printing chars:
+ #line=${line#$'\E[00m'}
+ # lines like "total 123M", we don't want
+ if [[ ! $line =~ ^total\ [0-9][^\ ]*$ ]]; then
+ lines+=("$line")
+ if ! [[ $line == [-dscbl][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xtT]* ]]; then
+ hardlinks=
+ else
+ [[ ! ${line:10:1} == " " ]] && aclchar=true
- # we also need to parse the hardlinks on the first pass, because for
- # example ls could see the highest count as 11, and thus use 3
- # places for hardlinks, " 10", but then we use 9 or 8 for a more
- # useful count, and would then use 2 places. So we have to look
- # through them all because we can't rely on the spacing that ls
- # decided on.
- y="${line:11}"
- initial_space="${y%%[![:space:]]*}"
- hardlinks="${y#$initial_space}" # remove any initial spaces
- hardlinks="${hardlinks%%[[:space:]]*}" # remove everything beyond first word
- # ignore the hardlinks that files/dirs always have
- if [[ ${line:0:1} == d ]]; then
- hardlinks=$(( hardlinks - 2 ))
- else
- hardlinks=$(( hardlinks - 1 ))
- fi
- [[ $hardlinks == 0 ]] && hardlinks=
- if (( ${#hardlinks} > max_hl_digits )); then
- max_hl_digits=${#hardlinks}
- fi
-
- hl+=("$hardlinks")
-
+ # we also need to parse the hardlinks on the first pass, because for
+ # example ls could see the highest count as 11, and thus use 3
+ # places for hardlinks, " 10", but then we use 9 or 8 for a more
+ # useful count, and would then use 2 places. So we have to look
+ # through them all because we can't rely on the spacing that ls
+ # decided on.
+ y="${line:11}"
+ initial_space="${y%%[![:space:]]*}"
+ hardlinks="${y#$initial_space}" # remove any initial spaces
+ hardlinks="${hardlinks%%[[:space:]]*}" # remove everything beyond first word
+ # ignore the hardlinks that files/dirs always have
+ hardlinks=$(( hardlinks - 1 ))
+ [[ $hardlinks == 0 ]] && hardlinks=
+ if (( ${#hardlinks} > max_hl_digits )); then
+ max_hl_digits=${#hardlinks}
fi
- done< <( "$binls" -lAh --color=always "--time-style=+%m-%d %Y
+ fi
+ hl+=("$hardlinks")
+ fi
+ done< <( "$binls" -lAh --color=always "--time-style=+%m-%d %Y
%m-%d %I:%M %P" "$@" )
- hardlink_spacing=$((max_hl_digits + 1))
-
- for index in "${!lines[@]}"; do
- line=${lines[index]}
- hardlinks=${hl[index]}
- if ! [[ $line == [-dscbl][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xtT]* ]]; then
- printf "%s\n" "$line"
- else
- perm=0
- for (( x=0; x<=8; x++ )); do
- y=${line:$(( -1*x + 9 )):1}
- [[ $y == [tT] ]] && perm=$(( perm + 512 ))
- if [[ $y == [sS] ]]; then
- [[ $x == 3 ]] && perm=$(( perm + 1024 ))
- [[ $x == 6 ]] && perm=$(( perm + 2048 ))
- fi
- [[ $y != [-ST] ]] && perm=$(( perm + 2**x ))
- done
- if $aclchar; then
- y="${line:11}"
- else
- y="${line:10}"
- fi
- middle=${y#*[^ ]* }
- size=${middle#*[^ ]* *[^ ]* }
- middle=${middle%"$size"}
- tail=${size#*[^ ]* }
- size=${size%"$tail"}
- declare -i sizePadding="${#size} - 1"
- size=( $size ) # remove spaces
- size=${size/.?/}
-
- printf "%s%4o%${hardlink_spacing}s%s%${sizePadding}s%s\n" \
- "${line:0:1}" $perm "$hardlinks" " $middle" "$size" " $tail"
- fi
+ hardlink_spacing=$((max_hl_digits + 1))
+
+ for index in "${!lines[@]}"; do
+ line=${lines[index]}
+ hardlinks=${hl[index]}
+ if [[ $line != [-a-zA-Z][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xtT]* ]]; then
+ # line we don't understand
+ printf "%s\n" "$line"
+ continue
+ fi
+ perm=0
+ for (( x=0; x<=8; x++ )); do
+ y=${line:$(( -1*x + 9 )):1}
+ [[ $y == [tT] ]] && perm=$(( perm + 512 ))
+ if [[ $y == [sS] ]]; then
+ [[ $x == 3 ]] && perm=$(( perm + 1024 ))
+ [[ $x == 6 ]] && perm=$(( perm + 2048 ))
+ fi
+ [[ $y != [-ST] ]] && perm=$(( perm + 2**x ))
done
-}
+ if $aclchar; then
+ y="${line:11}"
+ else
+ y="${line:10}"
+ fi
+ middle=${y#*[^ ]* }
+ size=${middle#*[^ ]* *[^ ]* }
+ middle=${middle%"$size"}
+ tail=${size#*[^ ]* }
+ size=${size%"$tail"}
+ declare -i sizePadding="${#size} - 1"
+ size=( $size ) # remove spaces
+ size=${size/.?/}
+ printf "%s%4o%${hardlink_spacing}s%s%${sizePadding}s%s\n" \
+ "${line:0:1}" $perm "$hardlinks" " $middle" "$size" " $tail"
+ done
+}