HiveBrain v1.2.0
Get Started
← Back to all entries
patternbashMinor

An attempt at making a neat ls

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
neatmakingattempt

Problem

This is a simple script to make ls output first folders, then files, then other stuff (symlink). I think this is really neat and would like to share the script in exchange for comments.

It should be noted that I'm working with GNU findutils and coreutils.

Some basic criteria that I'm aiming for include:

  • lssort must accept the same arguments as ls



  • lssort should depend on bash, find, ls and xargs



-
the output must not be prefixed with "./"

Problems:

  • arguments can not be augmented (-CFXtrs does not work)



Script

``
#! /bin/bash
#source $HOME/.scripts/string_manipulation.mergeme
function trim {
local var="$@"
#$ var=" hello space "
#$ echo ">"
#$ >
var="${var#"${var%%[![:space:]]*}"}" # rm leading whitespace characters
var="${var%"${var##*[![:space:]]}"}" # rm trailing whitespace characters
echo -n "$var"
}

function arrayContains {
# remove the shortest needle from array and compare lengths
# needs more testing...
declare -a array
declare -a arrayLess
declare needle
array=( "${!1}" ); shift # expand the passed array name
needle="$@" # try to match the rest as a string
arrayLess=(
trim "${array[@]#${needle}}"` ) # remove value $needle
if [ ${#array[@]} -eq 0 ];then return 1 ;fi
if [ ${#array[@]} -eq ${#arrayLess[@]} ];then return 1 ;fi
if [ ${#array[@]} -lt ${#arrayLess[@]} ];then return 1 ;fi
return 0
}

# d dir f file p named pipe (FIFO) l sylink s socket D door (Solaris)
# c character (unbuffered) special b block (buffered) special

function finddirs {
find "$@" \
-maxdepth 1 -depth -type d \
-regextype gnu-awk -regex "$REGEX" \
-printf '%f\0'
}
function findfiles {
find "$@" \
-maxdepth 1 -depth -type f \
-regextype gnu-awk -regex "$REGEX" \
-printf "%f\0"
}
function findspecials {
find "$@" \
-maxdepth 1 -depth \( -type l -o -type p -o -

Solution


  • If you use getopt or getopts to parse the options, you can use multiple options together, and it would simplify your code quite a bit. Example.



  • When you create arrays explicitly (name=(values)) you don't need to declare it first.



  • arrayContains should take one needle and then the contents of the array in question. This is how it works in other languages, and is easier to program and more explicit than using an array reference and concatenated needles. Also, when expanding needle the values will be concatenated with a space between them, which is rather arbitrary.



  • If needle is empty, arrayContains should return 0 regardless of the array contents.



-
You can merge the if statements in arrayContains by separating them with -o:

if [ $a -eq 0 -o $b -eq 0 -o $c -eq $a ]


-
ls works with globs rather than regular expressions by default. The find* functions will not return the same filenames as ls with the same input. For example, run touch example && mkdir eclectic and compare

ls [e]*


with

find . -maxdepth 1 -depth -type f -regextype gnu-awk -regex "[e]*"


  • You should be able to get the current working directory with $PWD rather than pwd to save a fork. This makes the code a tiny bit faster, without losing clarity, and fixes a very common but little known bug: The `command construct (identical to the preferred $(command) when using Bash) removes newlines from the end of the command output. That means if you mkdir $'foo\n' && cd $'foo\n' , ORIGIN_DIR="pwd" will give the wrong result, but ORIGIN_DIR="$PWD" will give the right one.



  • Use More Quotes, for example do ARG="$1" to enable working with whitespace in filenames. This is a very tricky subject.



  • If you want to get this working with all filenames, you'll need to use an array to store the find results before sending them to ls`

Code Snippets

if [ $a -eq 0 -o $b -eq 0 -o $c -eq $a ]
find . -maxdepth 1 -depth -type f -regextype gnu-awk -regex "[e]*"

Context

StackExchange Code Review Q#15627, answer score: 2

Revisions (0)

No revisions yet.