4

When I attempt to tab complete a filename in the bash prompt, and the output would be many (>100) filenames, the shell asks Display all <num> possibilities? (y or n)

Can I replicate this behaviour with the normal ls command?

I have a directory with >400k files. Accidentally lsing this directory leaves me with an unresponsive shell for many minutes while it spews output. Ctrl-C doesn't stop the flood.

1 Answers1

2

My solution might be a little heavy but well, who knows.

The problem is that, in order to do that, you would need to count files... which is usually done with:

$ ls | wc -l

Now, because the content is written to a pipe, and not to stdout, this will take a little less time to complete (writing to a terminal takes a little bit of time, which can become significant with 400k calls to write(2)). It might still take some time, but nevertheless, you could implement this with a simple test:

#!/bin/bash

LIMIT=1000
n=$(ls "$@" 2>/dev/null | head -n$LIMIT | wc -l)

if [ $n -ge $LIMIT ]; then
    echo "This directory contains more than $LIMIT entries."
    echo -n "Are you sure want to continue? (y/N) "

    read confirm
    if [ "$confirm" = "y" -o "$confirm" = "Y" ]; then
        exec ls "$@"
    fi
else
    exec ls "$@"
fi

Then, just set an alias in your .bashrc file, to call this script instead of /bin/ls:

alias ls='/path/to/my_ls.sh --color=auto'

Also make sure it is properly chmoded:

$ chmod +x /path/to/my_ls.sh

Edit: I added a call to head so that ls is terminated after $LIMIT lines of output. This should save some time (and actually make it a better solution that the timeout one) ;)

Another approach I would suggest is to use a timeout. If ls doesn't complete in say 5s, it should stop and ask you if you're sure about it. Here's a example script (valid for several commands, not just ls) :

#!/bin/bash                                                                                                                                                                                

TIMEOUT=5

if [ $# -le 0 ]; then
    echo "Usage: $0 [command]"
    exit 1
fi

# Try it silently for a few seconds...
timeout $TIMEOUT "$@" >/dev/null 2>&1

if [ $? -eq 124 ]; then
    echo -n "Your command failed to run under "$TIMEOUT"s. "
    echo -n "Retry without a timeout? (y/N) "

    read confirm
    if [ "$confirm" = "y" -o "$confirm" = "Y" ]; then
        exec "$@"
    fi
else
    exec "$@"
fi

Then you could set a few aliases, such as:

alias ls='/path/to/script.sh ls --color=auto'
alias grep='/path/to/script.sh grep --color=auto'
alias cat='/path/to/script.sh cat'

Note that I'm testing $? -eq 124 because...

If the command times out, and --preserve-status is not set, then exit with status 124.

See timeout(1) for more information.

John WH Smith
  • 15,880