micka190
micka190

Reputation: 953

Display Only Files And Dotfiles In The Default "ls" Command Format?

EDIT 2: For those with the same question, please be sure to read the comments on the accepted answer, as they go more in depth about how to properly use the command. I will post the final script once it is done with my own explanation of what is going on.

I'm trying to write a simple bash function that clears the terminal screen, then performs multiple ls commands to show the different content types of the current directory in different colors.

I've managed to write something in Python which kind of does the same thing, but it has some big drawbacks (specifically, the behavior for special characters in filenames is "iffy" on Cygwin, and it's a pain to make it fit properly on the screen). I want it to look something like this:

Python Example

*With non-dotfiles in green (haven't include them in the screenshot for privacy reasons).

I've managed to list both hidden directories and visible directories, but the files are giving me trouble. Every method of using ls to get all files I've tried uses the -l argument, or something like find or grep, all of which output the files in a single column (not what I want).

Is it possible to use the ls command (or something else) to output only the files or dotfiles of a directory while keeping ls's default output format?

EDIT 1: Here's what the script currently looks like (not much yet, but some people want to see it)

function test() {
    clear;

    GOLD=229;
    RED=203;
    BLUE=39;
    WHITE=15;
    GREEN=121;

    # Colored legend.
    tput sgr0;
    tput setaf bold
    # echo's "-n" suppresses the new line at the end.
    tput setaf $GOLD;
    echo -n "Working Directory ";
    tput setaf $RED;
    echo -n "Hidden Directories ";
    tput setaf $BLUE;
    echo -n "Visible Directories ";
    tput setaf $WHITE;
    echo -n "Hidden Files ";
    tput setaf $GREEN;
    echo "Visible Files";

    pwd;

    ls -d .*/; # List hidden directories.
    ls -d */; # List visible directories.
    # List dotfiles.
    # List files.
}

Upvotes: 0

Views: 1558

Answers (3)

micka190
micka190

Reputation: 953

Using the answer provided by matzeri, I managed to get a nice start. That answer is still accepted, because it answered the question that I asked. Here's the current version of the script, which I've turned into a function for easy access. I'm gonna go through it and explain some things that needed to be modified from the answer to give me the result I wanted, but didn't specifically ask for.

c() {
    stty -echo; # Disable keyboard inputs during function.

    local HAS_DOTDIRS=false;
    local HAS_DIRS=false;
    local HAS_DOTFILES=false;
    local HAS_FILES=false;

    local BLUE=39;
    local GOLD=229;
    local GREEN=121;
    local PINK=170;
    local RED=203;
    local WHITE=15;

    local CYG_CWD=$(pwd);
    local WIN_CWD=$(cygpath -w "$(pwd)" | sed 's/\\/\//g');

    local DOTDIRS=$(ls -Ad .*/); # Get directories with '.' prefix.
    local DIRS=$(ls -Ad */); # Get normal directories.
    local DOTFILES=$(find .  -maxdepth 1 -type f -name ".*" | sed 's/^..//');
    local FILES=$(find .  -maxdepth 1 -type f -not -name ".*" | sed 's/^..//');

    clear;
    tput sgr0;
    tput setaf bold;

    local LEGEND="$(tput setaf $GOLD)Cygwin Working Directory ";
    LEGEND+="$(tput setaf $PINK)Windows Working Directory ";

    if ! [ -z "$DOTDIRS" ] ; then
        HAS_DOTDIRS=true
        LEGEND+="$(tput setaf $RED)Hidden Directories ";
    fi

    if ! [ -z "$DIRS" ] ; then
        HAS_DIRS=true
        LEGEND+="$(tput setaf $BLUE)Visible Directories ";
    fi  

    if ! [ -z "$DOTFILES" ] ; then
        HAS_DOTFILES=true
        LEGEND+="$(tput setaf $WHITE)Hidden Files ";
    fi

    if ! [ -z "$FILES" ] ; then
        HAS_FILES=true
        LEGEND+="$(tput setaf $GREEN)Visible Files";
    fi

    echo $LEGEND; 
    echo "";

    echo "$(tput setaf $GOLD)$CYG_CWD";
    echo "$(tput setaf $PINK)$WIN_CWD";

    tput setaf $RED
    ls_list "$HAS_DOTDIRS" "$DOTDIRS"

    tput setaf $BLUE
    ls_list "$HAS_DIRS" "$DIRS"

    tput setaf $WHITE
    ls_list "$HAS_DOTFILES" "$DOTFILES"

    tput setaf $GREEN
    ls_list "$HAS_FILES" "$FILES"

    tput sgr0;

    stty echo; # Enable keyboard inputs after function.
}

ls_list() {
    # $1 - boolean - condition to print
    # $2 - list - names to 'ls'
    if $1 ; then
        echo "$2" | xargs -d '\n' ls -d;
    fi
}

Firstly, I wrapped the whole thing with stty to stop the user from messing-up the output with keyboard inputs.

Then, I create booleans that will allow the ls outputs at the end to be performed with a very minimal delay between them (if [ bool ] rather than checking the status of a list).

The color variables are simply used by the tput command, which lets you change the color of the terminal's text.

Note: Some of the color values might be off, that's because I had issues with my Cygwin theme (currently using Nord), and these values look like their names on my setup. Your mileage may vary.

To fellow Cygwin users: I've had a few issues with tput not working when it would've worked on an actual Linux machine. If you're having trouble with tput consider defining the LS_COLORS (which can be formatted to accept RGB values), and setting the CLICOLOR variable to true, then exporting both. This can replace the tput command, although you now might want to store the initial value of both, and you have to export the LS_COLORS variable constantly.

The CYG_CWD and WIN_CWD are there because Cygwin paths aren't the same as Windows paths, and I like having both. The sed command replaces Windows' "\" with "/" for easy copy+paste.

This is where the answer to the question starts. The DOTDIRS, DIRS, DOTFILES, and FILES variables will get lists.

The ls command already allows us to filter just directories and directories starting with ., via the ls -Ad */ and the ls -Ad *./ commands. ls ignores files starting with ., unless specified (like I do), ending with / searches for directories, and the -d parameter makes it name directories instead of showing us what's in them.

As for dotfiles and regular files, ls doesn't really let us get them, because you can't specify a character which defines a file like you can with / for directories. Instead, we can use the find . -maxdepth -type f with the appropriate use of the -name or -not -name arguments to filter our files. There is an issue, however: the command will prefix the results with ./. So .bashrc becomes ./.bashrc. I don't want that. The output is thus piped to the sed command, which substitutes the first two characters for nothing (effectively removing them).

From here, we simply determine which list has elements, set the appropriate boolean values to ensure quick ls outputs at the end, print the legend, all the while changing the colors the terminal uses with the tput command.

Eventually, the ls_list function is called. If the given boolean is true, it performs the echo "$2" | xargs -d '\n' ls -d command. Essentially, it echoes the list of items found, which is piped into the xargs -d '\n' command. xargs allows us to pass the values piped to it as parameters for the ls command. The -d '\n' arguments changes the separator from spaces to newlines, because some names might have spaces in them. What ls does, when you give it filenames, is simply prints them with the regular ls output format (which is exactly what I want). I added the -d parameter so that is names directories instead of showing their contents, since I'm using that function for all my lists.

Finally, reset whatever tput is with sgr0 and re-enable keyboard inputs with stty!

This is what the output looks like for my /home directory.

enter image description here

Final notes:

It feels slightly slower than my Python script, but it won't bug-out with special characters in filenames and it will always respect the terminal's window size, since that's what ls does. I think the "slowness" might be because the 4 xargs ls operations don't print at the same time, while the python one printed everything at once. I can live with it being 1-2 seconds vs 0.5-1 second.

Upvotes: 0

hnicke
hnicke

Reputation: 602

For non-hidden files and directories, try

ls -d *

For hidden files and directories, use

ls -d .*

Upvotes: 0

matzeri
matzeri

Reputation: 8496

to list only the dot files in the current directory

find .  -maxdepth 1 -type f -name ".*" | xargs ls --color=tty

to list only the other files

find .  -maxdepth 1 -type f -not -name ".*" | xargs ls --color=tty

Upvotes: 1

Related Questions