Created
February 8, 2012 03:41
-
-
Save rsvp/1765163 to your computer and use it in GitHub Desktop.
lstest.sh : list files by type (regular, directory, symbolic link, named pipe, etc.), and optionally show detailed information unavailable via the ls command (while also properly handling dot files). Linux bash script.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# bash 4.1.5(1) Linux Ubuntu 10.04 Date : 2012-02-04 | |
# | |
# _______________| lstest : list files by type, optionally show further info. | |
# | |
# Usage: lstest [test flag] [-0|-1|-A|-i|-I|-a] [--count] | |
# | |
# ^attributes | |
# ^terse "stat" | |
# ^info via "stat" | |
# ^All files; default. | |
# ^only non-dot files. | |
# ^only dot files. | |
# | |
# ^test flag uses Bash built-in "test", so e.g.: | |
# -f regular file (default flag) | |
# -s non-zero size file | |
# -d directory | |
# -h symbolic link (-l unofficially here) | |
# -p named pipe | |
# -S socket | |
# -r readable | |
# -w writable | |
# -x executable | |
# -e file exists (all types will show up) | |
# etc. | |
# | |
# The arguments take their meaning positionally. | |
# Filtered items from stdout can be piped to a regex program. | |
# --count of such items can be shown via stderr | |
# (useful for logs; otherwise consider pipe to wc -l). | |
# Count of zero is considered fatal. | |
# | |
# Examples: % lstest | |
# # list only regular files including such dot files. | |
# % lstest -d -0 --count | |
# # list and count only dot directories. | |
# % lstest -d | grep '^[A-Z]' | |
# # list all directory names beginning with upper case. | |
# % lstest -f -i | |
# # display detailed info on all regular files, | |
# # including access/modify/change times with inode. | |
# % while read i ; do readlink "$i" ; done < <(lstest -h) | |
# # list the values of symbolic links | |
# # (without parsing on "->" :-) | |
# # but see the Easter egg below ). | |
# | |
# Dependencies: stat, readlink, lsattr. | |
# CHANGE LOG LATEST version available: https://bitbucket.org/rsvp/gists/src | |
# | |
# 2012-02-04 Add second arg -I to show terse file stat info; | |
# second arg -a to show file attributes using lsattr -d. | |
# 2012-02-03 Use case on second arg for faster execution, | |
# now also allows exclusively dot files to show. | |
# Add another second arg -i to show detailed file stat info. | |
# 2012-02-02 First version originates from frustration that ls has no flag | |
# for showing ONLY regular files. | |
# Most solutions use: ls and grep ^- combo, | |
# or even: ls and awk combo, | |
# or recursive hammer: find with -maxdepth | |
# (see example at the end). | |
# DOT files will receive special handling, | |
# esp. the notorious pair "." and ".." ;-) | |
# | |
# MAIN IDEA: simply use Bash test as filter. So let's build on: | |
# for f in *; do [[ -f "$f" ]] && echo "$f"; done | |
# _____ PREAMBLE: settings, variables, and error handling. | |
# | |
LC_ALL=POSIX | |
# locale means "ASCII, US English, no special rules, | |
# output per ISO and RFC standards." | |
# Esp. use ASCII encoding for glob and sorting characters. | |
set -e | |
# ^errors checked: immediate exit if a command has non-zero status. | |
set -u | |
# ^unassigned variables shall be errors. | |
arg1=${1:-'-f'} | |
# ^regular file as default. | |
[ "$arg1" = "-l" -o "$arg1" = "-h" ] && arg1="-L" | |
# Convert officially for symbolic link. | |
arg2=${2:-'-A'} | |
# ^All including dot files as default, else use -1. | |
arg3=${3:-'--no-count'} | |
# --count would show summary to stderr. | |
program=${0##*/} # similar to using basename | |
cleanup () { | |
: | |
} | |
# -------------------------------------------------------------------- | |
warn () { | |
# Message with basename to stderr. Usage: warn "message" | |
echo -e "\n !! ${program}: $1 " >&2 | |
} | |
# -------------------------------------------------------------------- | |
die () { | |
# WARN, CLEANUP, then EXIT with the status | |
# of most recent command or custom status. | |
# Usage: fatal_command || die "message" [status] | |
local status=${2:-"$?"} | |
warn "$1" && cleanup && exit $status | |
} | |
# -------------------------------------------------------------------- | |
# Clean INTERRUPT where 1=SIGHUP, 2=SIGINT, 3=SIGQUIT, 15=SIGTERM | |
trap "cleanup && die 'SIG disruption but cleanup OK.' 114" 1 2 3 15 | |
# | |
# _______________ :: BEGIN Script :::::::::::::::::::::::::::::::::::::::: | |
GLOBIGNORE=.:.. | |
# ^^^^ when matching all dot files, exclude . and .. | |
shopt -s dotglob | |
# ^set Let globs match hidden dot files. | |
shopt -s nullglob | |
# ^set just in case the current directory is empty, | |
# else the array will have one literal item '*' instead of zero. | |
n=0 | |
# ^initialize counter for qualified items. | |
items=( * ) | |
# ^an array which we will sweep through. | |
# echo "DEBUG: ${items[@]}" | |
[ ${#items[@]} -eq 0 ] && die "vacant directory: $PWD" 115 | |
case "$arg2" in | |
'-A') # both dot and non-dot files... | |
for i in "${items[@]}" ; do | |
[ "$arg1" "$i" ] && echo "$i" && n=$(( $n + 1 )) | |
done 2> /dev/null ;; | |
# Bad first argument causes verbose mess. | |
'-0') # only dot files... | |
for i in "${items[@]}" ; do | |
# faster than grep | |
[ "$i" != "${i/#./}" ] && \ | |
[ "$arg1" "$i" ] && echo "$i" && n=$(( $n + 1 )) | |
done 2> /dev/null ;; | |
# Bad first argument causes verbose mess. | |
'-1') # only non-dot files... | |
for i in "${items[@]}" ; do | |
# faster than grep | |
[ "$i" = "${i/#./}" ] && \ | |
[ "$arg1" "$i" ] && echo "$i" && n=$(( $n + 1 )) | |
done 2> /dev/null ;; | |
# Bad first argument causes verbose mess. | |
'-i') # both dot and non-dot files, get detailed stat info. | |
# Report inode and file times, record delimited by @@. | |
for i in "${items[@]}" ; do | |
[ "$arg1" "$i" ] && stat "$i" && \ | |
echo "@@" && n=$(( $n + 1 )) | |
done 2> /dev/null ;; | |
# Bad first argument causes verbose mess. | |
'-I') # both dot and non-dot files, get terse stat info | |
# on one line (useful for post-processing). | |
for i in "${items[@]}" ; do | |
[ "$arg1" "$i" ] && stat -t "$i" && \ | |
n=$(( $n + 1 )) | |
done 2> /dev/null ;; | |
# Bad first argument causes verbose mess. | |
'-a') # both dot and non-dot files, get file attributes | |
# using lsattr ("man chattr" for their meanings). | |
for i in "${items[@]}" ; do | |
[ "$arg1" "$i" ] && lsattr -d "$i" && \ | |
n=$(( $n + 1 )) | |
done 2> /dev/null ;; | |
# Bad first argument causes verbose mess. | |
'-v') # Easter egg: get value of every symbolic link... | |
# -L or -h or -l as first argument will do: | |
[ "$arg1" = "-L" ] && \ | |
for i in "${items[@]}" ; do | |
[ "$arg1" "$i" ] && readlink "$i" && n=$(( $n + 1 )) | |
done 2> /dev/null ;; | |
# Bad first argument causes verbose mess. | |
*) die "undefined second arg: $arg2" 117 ;; | |
esac | |
[ $n -eq 0 ] && die "NONE match $arg1 $arg2 in $PWD" 113 | |
[ "$arg3" = "--count" ] && warn "$n match $arg1 $arg2 in $PWD" | |
cleanup | |
exit 0 | |
# _______________ EOS :: END of Script :::::::::::::::::::::::::::::::::::::::: | |
# Scripts which parse ls output should definitely | |
# be avoided due to portability issues and the | |
# overhead of properly handling weird filenames | |
# which include spaces. | |
# | |
# The nearest acceptable alternative would be: | |
# | |
# find . -maxdepth 1 -type f | |
# | |
# where "find" supports the following major "-type" | |
# f regular file | |
# d directory | |
# l symbolic link | |
# p named pipe | |
# s socket | |
# and selector options such as -executable are available. | |
# Filtering by file size can be fine-tuned. | |
# | |
# It also handles dot files but includes "." as directory. | |
# From its stdout we would have to generally delete | |
# the prefix "./" and also sort the results for appearance. | |
# To filter further, regex is available as an option, e.g. see -iregex. | |
# | |
# Unfortunately, in the case where the number of filtered | |
# items is zero, or where the current directory is empty, | |
# "find" signals success by an exit 0. Sure, we could examine | |
# an external output file, and do all the tweaks... | |
# at the cost of simplicity and execution speed. | |
# vim: set fileencoding=utf-8 ff=unix tw=78 ai syn=sh : |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment