2

Is it possible to have the ls command behave differently based on the number of directory entries that may be listed?

If I just use ls (with no modifying options, but I can specify directories or filters), I want it to:

  • apply -l long listing format if there are 10 entries or less
  • show the first 50 entries only and output a warning that there are x more entries

Is this possible? How can I do this?

Note that I don't want to use a custom script command switch to a custom command - I am OK with a custom script or wrapper, but I still want to use ls to do this, with full functionality still maintained. That is, not my-custom-ls but just ls to call the script/wrapper.

6
  • 2
    Ls doesn't care how many files there are. Closest I can think of would be a wrapper function.
    – Jeff Schaller
    Mar 25, 2016 at 11:26
  • @JeffSchaller I clarified question. Can you show me how?
    – ADTC
    Mar 25, 2016 at 14:10
  • 1
    ll() { ls -I "$@" | less; } is similar, and much simpler and more predictable.
    – Mikel
    Mar 25, 2016 at 14:20
  • why not just use the unix command line as it's meant to be used and learn how to do things like ls -l | head -n 50 ? what you want to do is possible with a lot of stuffing around (e.g. hacking and recompiling the ls source) but, more importantly, is not something you should want to do. success is likely to break many other things that expect ls to behave as the man page documents it to behave. Witness the recent complaints about GNU ls changing the default output to --quoting-style=shell-escape or whatever it was changed to....it's arguably a much better output format but (cont)
    – cas
    Mar 25, 2016 at 23:47
  • ... shouldn't be the default because it breaks too many existing scripts that depend on the old literal quoting style.
    – cas
    Mar 25, 2016 at 23:48

5 Answers 5

4

If you don't want a custom script command/wrapper function, then you want a custom binary command. GNU ls source code is available, along with other implementations.

Create your own patched version of ls and you'll be set.

Note that this change in behavior would break POSIX compliance.

Edit (according to your revised question):

ls() {
    if [ $(command ls "$@" | wc -l) -gt 10 ]; then
        command ls "$@" | head -50
    else
        command ls -l "$@"
    fi
}

Enhanced version that shows the number of remaining entries:

ls() {
    [ ! -t 1 ] && { command ls "$@" ; return ; }
    r=$(command ls "$@")
    lines=$(printf "%s" "$r" | wc -l)
    if [ $lines -gt 10 ]; then
        printf "%s" "$r" | head -50
        if [ $lines -gt 50 ]; then
          printf "%d more entries\n" $((lines - 50))
        fi
    else
        command ls -l "$@"
    fi
}
2
  • Well, I think I should clarify. Edit: Done. Please check question.
    – ADTC
    Mar 25, 2016 at 14:05
  • 1
    Might be good to have a check for output to a terminal ([[ -t 1 ]]) at the beginning and only use the custom behavior if it is. I have a similar function for grep that adds color and line numbers if and only if output is straight to my terminal.
    – Kevin
    Mar 25, 2016 at 16:28
0

Create your own script, e.g. in Python, that counts the arguments and calls ls(1) with the right arguments and spruces it up with more(1) for paging as needed (if output goes to a tty).

1
  • What do you mean "counts the arguments"?
    – ADTC
    Mar 25, 2016 at 14:05
0

I can't redefine ls itself but you can use this function for an 'ls2':

ls2 () {
  num_of_files=`ls | grep "\/$" | wc -l`
  if [ "$num_of_files" -gt "10" ]; then
    ls | less
  else
    ls -l
  fi  
}

to create a custom command ls2 that uses grep to look for directories and then wc (unix word count function with number of lines option) to count how many. You can change how many files in the "10" comparison. You can change what is actually done by changing the ls -l and/or ls -a commands

If you are not familiar with bash functions - put the code in a file, then chmod +x the file to make it executable, then run the file with . the_filename and then use the function with ls2

For a permanent solution add the function to your .bashrc file (and consider creating a file that is sourced from .bashrc that so you can start add more functions in one spot and not make your .bashrc file ungainly.

I can't get less to show entries at a time.

3
  • jlliagre has redefined ls. The trick is to use command ls in the function body.
    – ADTC
    Mar 25, 2016 at 14:45
  • Also, why not ls -1 | wc -l? Besides, when I say "directory entries" I don't mean the directories in the current directory. I mean the entries in the current directory which include, but isn't limited to, files, directories and symlinks.
    – ADTC
    Mar 25, 2016 at 14:57
  • ATDC, FYI ls | wc -l is actually strictly equivalent to ls -1 | wc -l as ls switch to its one file per line behavior if the output is not a terminal.
    – jlliagre
    Mar 25, 2016 at 15:57
0

One thing you could do is put each listing on a seperate line and then use wordcount to count lines and have an if else fi statement depending on the number of files.

files=$(ls "$@" | wc -l)
if [ $files -ge 50 ]
then
something
fi

sorry if this is not what you are looking for.

1
  • you could set this as a function in your bash profile or bashrc Mar 25, 2016 at 20:54
0

Here's my interpretation of the question. It is bash-specific, as it uses an array. It defines a function named ls (so be sure to run unalias ls to clear the way for it to execute as just ls). The function creates an array of the given arguments (or of the current directory contents if no arguments are given); it then counts those elements to determine which ls flags to use (less than 10, use ls -ld; otherwise use ls -d); if there are more than 50 files, only the first 50 are shown, and a message is shown. I also brazenly copied the idea from Kevin/jlliagre to have the function ls behave more normally when the output is not to a screen.

This does not allow for flags to be passed to the real ls; things like -r and -a would need special care.

function ls {
  _ls_longlimit=10
  _ls_cutoff=50
  declare -a _ls_files

  [ ! -t 1 ] && { command ls "$@"; return; }
  # TODO: skip past any "-rtxyz" options
  if [ $# -eq 0 ]
  then
    _ls_files=(*)
  else
    for a in "$@"
    do
      if [ -d "$a" ]
      then
        # add "${a}/*" to array
        _ls_files+=("${a}"/*)
      else
        # add $a to array
        _ls_files+=("$a")
      fi
    done
  fi
  if [ ${#_ls_files[@]} -le $_ls_longlimit ]
  then
    command ls -ld "${_ls_files[@]}"
  else
    # -1 because arrays are 0-based
    command ls -d "${_ls_files[@]:0:_ls_cutoff - 1}"
  fi
  if [ ${#_ls_files[@]} -gt $_ls_cutoff ]
  then
    echo $((${#_ls_files[@]} - _ls_cutoff)) more entries
  fi
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .