Remap ls to honor .hidden files

I wrote this code in my .bashrc to remap ls to honor .hidden files. I'm pretty new to shell/bash scripting, so I feel like this is not the most performant way to handle this, and it has some limitations. I would appreciate any suggestions regarding performance improvements, limitation reduction, or common best practices for shell/bash scripting (or improving my post since I'm new to the StackExchange network).

# File names in .hidden files are ignored by ls
# NOTES: Not tolerant of empty lines (or lines with only whitespace) in the .hidden file(s)
#    If any argument doesn't begin with a '-', this assumes it's a file argument
#    If you pass multiple folders, it will essentially concatenate the .hidden files and ignore all files matching any of those names (so if you have a file name TEST in two folders, but only have TEST in the .hidden file of one folder, calling ls on both folders won't show either TEST file)
#    It does honor the -a flag
#    I don't know if there's a limit on total number of flags or total command length, but if there is, this may surpass it depending on the number of file names in the .hidden file
ls () {
DEFAULT_ARGS="--color=auto" # Here, because I want colors (and you can't have a function and an alias with the same name)
ALL=false
A=0
F=0
I=0
declare -a ARGS
declare -a FILES
declare -a IGNORE
for a in $@ do if [[$a =~ ^-(a|[^-]+a) ]]; then # If the -a flag is present
ALL=true
break
fi
if [[ $a =~ ^[^-].* ]]; then # If the argument doesn't start with '-', add it to the Files array FILES[$F]=$a ((F++)) else # Otherwise add it to the Args array ARGS[$A]=$a ((A++)) fi done if$ALL; then
command ls ${DEFAULT_ARGS}${@}
return
else
if [ ${#FILES[@]} -eq 0 ]; then # If no files/directories are present, explicitly add the implied . FILES[$F]="."
fi
for f in ${FILES[*]} do if [ -f "${f}/.hidden" ]; then # Read the .hidden file, if present, and generate the -I flags
IGNORE[$I]=$(awk '{print "-I " $0}' "${f}/.hidden" | tr "\n" " ")
((I++))
fi
done
command ls ${DEFAULT_ARGS}${ARGS[*]} ${IGNORE[*]}${FILES[*]}
return
fi
}


Note: A .hidden file is a file (named .hidden) containing a list of file names from the same directory that should be treated as if they begin with a '.'

1 Answer

Some suggestions:

• Upper case names are by convention reserved for exported variables.
• Use More Quotes™, especially around $@ • Use arrays for constructing commands • Variables are by default declared in the global scope. It would be better to declare the variables in the function local. • Use getopt to parse options - the default pattern is a bit wonky but it's easier to deal with for complex cases. • You can use readarray to read .hidden into an array in a single command. • The returns are redundant. • in$@, or more correctly in "$@", is redundant. for argument does the same thing. • You don't need semicolons if you put then on the next line like you already do with do. • You shouldn't have to use index variables. For example, both times $F is used you can instead use FILES[${#FILES[@]}]=… • $* and its accomplice ${name[*]} split every argument on $IFS, and is therefore extremely rarely useful. Use $@ and ${name[@]} instead.
• Thanks! Also, that wiki looks like it'll be a incredibly useful resource. Thanks for sharing that. – UrsineRaven Jul 1 at 16:06