leo charre


Setting LS_COLORS colors of directory listings in bash terminal

This one I underestimated the importance of. But no more.

If you really use vim/perl/unix as I do- you are in bed with the command line. You're looking through a lot of file listings, regularly.

Using most guis, such as kde/gnome on ubuntu/fedora/suse/debian.. You'll notice when you do an ls on a directory- you most likely, by default, get some funny ephemera manifestations. What I mean is, directories may be listed in a different color. Maybe they are bold. And regular files are normal text. And some of them are in different colors!

What's up with that? Do we really give a shit if every jpg is red and every gif is blue, pdfs are purple and then notice also- JPG is not detected as a file ext to highlight and jpg *is*. Try it, change the ext of a JPG file to jpg, the color changes.

What is up with that? Well- I had been pleasantly ignoring that since- forever. I never cared to meddle or alter the settings, or even to look if there was a setting to affect that.

But recently I've been really curious to learn not just more code- more programming- but more of *how to work* more efficiently. I looked up how to affect these color and text formatted listings of directories- hoping that it may be helpful when looking through distros, through filesystems.. And you know what.. it is. Do you remember using vim and then one day actually taking the time to search and replace, properly, with capture.. And.. Wasn't it really fucking useful?

Ok, this one I know you're gonna roll your eyes at. And I did too, for years. But I would like you to take ten minutes of your life to give this a ride and see for yourself that it does help out a little. And the life of the code... These little things add up- using find xargs grep, using cv, perl one liners- and I'm gonna have to vouch for this one too. Directory listing text formatting.

  1. Ok, the thing you are looking to alter is a shell environment variable- on bash it's LS_COLOR.
  2. What is it set at currently? Run set and grep out for LS_COLOR:
    $ set | grep LS_COLOR
    LS_COLORS='no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=00;35:*.cmd=00;32:*.exe=00;32:*.sh=00;32:*.gz=00;31:*.bz2=00;31:*.bz=00;31:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.t=93:*.pm=00;36:*.pod=00;96:*.conf=00;33:*.off=00;9:*.jpg=00;94:*.png=00;94:*.xcf=00;94:*.JPG=00;94:*.gif=00;94:*.pdf=00;91'
    
  3. Alright, wtf just happened. This string tells ls that when ls --color is called, these are the colors/text formatting to use.

    Now, ls --color? When have you run that flag? This is set as an alias in some bashrc file somewhere. Likely not your ~/.bashrc. But in some /etc/$BASHRC?? file. If you want to find out in which exact place you're defaulting to use ls --color ...

    $ find /etc/ 2>/dev/null | xargs grep -s 'alias ls'
    /etc/profile.d/colorls.sh:      alias ls='ls --color=tty' 2>/dev/null
    /etc/profile.d/colorls.csh:alias ls 'ls --color=tty'
    
    Now, how the heck is this being called??? Look at your ~/.bashrc, it may have an entry to include /etc/bashrc if it exists, and then.. well.. there's some other default profile voodoo and such I'm not versed in.
  4. How it works..

    The string value of the LS_COLOR environment variable is parsed as a hash, internally. The stream of garble is understood as.. The delimiter is the color (:) symbol. The first part is what the element of the directory listing is, then assignment (=), and a style code. More than one style code can be assigned.

    For the following chunk of string: di=00;1:fi=00:*.php=00;34, it means:

    di(directory) =(assignment) 00(normal text) ;(and) 1(bold)
    :(delimiter, next entry..)
    fi(file) =(assignment) 00(normal text)
    :(delimiter, next entry..)
    *.php(everything matching *.php) =(assignment) 00(normal text) ;(and) 34(blue)
    
  5. How to change it!

  6. All you need to do to see it change before your eyes, is to go to a command line prompt, and type in:

    $ LS_COLOR='di=00:fi=00;1:*.php=00;34'
    $ ls
    

    You'll notice that makes all regular files appear bold, all directories in 'normal text weight' and default color (black on white, white on black)- and php files are blue (or whatever color is mapped to that slot).

    Now, if you wanted to make that change permanent (instead of only to that one terminal session you just opened)- you would enter that into your ~/.bashrc file .. as:

    export LS_COLOR='di=00:fi=00;1:*.php=00;34'
    
  7. Knowing the codes:

    Please do note.. that's a pretty lousy string to use. It's just an example. You need to play around with it and figure out what you really want. For that, you need to know what the codes are.. for the filesystem elements (no,li,di.fi.. etc), for setting up regexes (*.pm)- and what the codes mean (1 bold, 00 normal, 9 strikethrough). I picked up a good list from Bartman.

    Here's that same list with some extras added- these work properly on GNU bash 3.0.

    0 = default colour
    1 = bold
    4 = underlined
    5 = flashing text
    6 = no change
    7  = reverse field
    8 = black
    9 = strikethrough (cool!)
    10 - 29= no change
    30  = light green
    31  = red
    32  = green
    33  = orange
    34  = blue
    35  = purple
    36  = cyan
    37  = grey
    38 = underline
    39 = no change
    40  = black background
    41  = red background
    42  = green background
    43  = orange background
    44  = blue background
    45  = purple background
    46  = cyan background
    47  = grey background
    90  = dark grey
    91  = light red
    92  = light green
    93  = yellow
    94  = light blue
    95  = light purple
    96  = turquoise
    100 = dark grey background
    101 = light red background
    102 = light green background
    103 = yellow background
    104 = light blue background
    105 = light purple background
    106 = turquoise background
    
  8. One way of managing colorschemes..

    Yup. You can have colorschemes for dir listings. Crazy, huh?

    All you would need to do is to save your colorschemes in files.

    For example in ~/.ls_color-joe

    export LS_COLORS='no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:ex=00;35'
    

    If you want to import that via the current shell, just use the '.' operator, the command would be:

    . ~/.ls_color-joe
    

    And now when you do a listing, it will use those colors. My god, though, why would you want to do that? What if you don't have the same ~/.bashrc in every system? You really want to copy and paste that string amongst various machines?

    What could be convenient is to store them as files, and call them into your current shell with the dot operator. Or.. You can do this in your ~/.bashrc as..

    if [ -f "~/.ls_color-joe"]; then . ~/.ls_color-joe; fi
    
  9. Making it easier to edit the color string

    Now.. I hate to edit a string like that.. 'no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:ex=00;35'.. Yuck! So, what I've done for myself is my colorscheme file let's me enter things like:

    # images
    *.jpg 35
    *.gif 35
    *.xcf 33
    
    # code
    *.sh 91
    *.pl 91
    

    The file is still called the same way as explained above, it's just a bash file.. But.. it has a little magic. It allows human formatted LS_COLORS strings to automatically be turned into the proper bash environment variable string.

    Or you could save the human readable format in a flat text file, for example ~/.mylscolors

    # HUMAN_FORMATTED_DATA
    # list one per line
    
    # these are basic filesystem items
    no 00          # normal
    fi 00          # file
    di 00 34       # directory
    ln 00 36       # link
    pi 40 33       # pipe
    so 00 35       #
    bd 40 33 01
    cd 40 33 01
    or 01 05 37 41
    mi 01 05 37 41
    ex 00 91       # executable
    *.ai  00 91 # adobe ill
    *.doc 00 91 # msword shit
    
    # data, such as .db, .csv
    *.csv    95
    *.dsv    95
    *.db     95
    *.sql    95
    *.meta   95
    # CONFS
    *.xml    95
    *.yaml   95
    *.yml    95
    *.conf   95
    # [a-z0-9]*rc
    
    # by now you should really know that everything after the # pound sign is a comment.. use vim, nice syntax highlighting
    

    And then use this command to turn it into the LS_COLORS string:

    $ cat ~/.mylscolors | grep '\w' | grep -v '^#' | sed 's/#.\+//' | perl -lane 'printf "%s=%s:", shift @F, join ";", @F;'
    

    What if you wanted to save that output to your ~/.bashrc ? Yes, you could copy and paste the output into your ~/.bashrc. But that is not the unix way...

  10. Doing it via the command line instead of cutting and pasting..

    What we do is redirect the output into the ~/.bashrc. First let's make sure we don't havbe a LS_COLORS string in there.. We can look inside via:

    $ cat ~/.bashrc | grep LS_COLORS
    

    If we do, let's just rip that right out..

    $ perl -p -i -e 's/^export LS_COLORS=.*$//' ~/bashrc
    

    Is perl too weird for you? We can use bash..

    $ cp ~/.bashrc ~/.bashrc.bak; cat ~/.bashrc.bak | grep -v LS_COLORS > ~/.bashrc
    

    The -v flag is match reverse, so no lines containing LS_COLORS will be spat out. Backing up the file is good, and also.. if you tried to to this to the file itself.. You're end up with a blank file:

    # THIS IS WRONG and will delete your file:
    cat ~/.bashrc | grep -v LS_COLORS > ~/.bashrc
    

    Ok, now we can turn the string into the variable, the source being human readable content.. and save it to the ~/.bashrc..

    $ echo "export LS_COLORS='"$(cat ~/tmp/testlscolor | grep '\w' | grep -v '^#' | sed 's/#.\+//' | perl -lane 'printf "%s=%s:", shift @F, join ";", @F;')"' # source was ~/tmp/testlscolor" >> ~/.bashrc
    

    Make sure you redirect the output as append '>>' and not overrite '>'.

    Great. What if we wanted to make this into a command? So we can use the source, the human legible format of a LS_COLORS string anytime?

    For that.. we need to more or less script all of our example...

  11. Making it all into commands

    The first thing we should do here, is separate this into two commands, one should be a unix filter, and the other.. should be the procedure we want .

    Now, a unix filter is the most important and useful kind of command you can write in unix. It simply takes standard input and spits to standard output. Read The Rule of Composition, it explains the idea behind filters.

    1. Making this a simple filter..

      Will make it so the process can be used for anything else.

      The cheap and simple way to do this, is do one of..

      a) Make it an alias in your ~/.bashrc
      This is a cheap and simple way to add commands to your environment. Aliases can be cool.

      In your ~/.bashrc, enter this line:

      alias string2lscolors="grep '\w' | grep -v '^#' | sed 's/#.\+//' | perl -lane 'printf \"%s=%s:\", shift @F, join \";\", @F;'"
      

      Now reload your config file '$ . ~/.bashrc'

      And let's see what aliases we have, you should see your newly created alias, run the command '$ alias' to see a listing.

      We can now use that as a filter on the command line.

      b) Save it to a bash script
      Edit a file in your ~/bin directory.. make sure it's got execute permissions..
      $ vim ~/bin/string2lscolors
      $ cat ~/bin/string2lscolors
      #!/bin/sh
      grep '\w' | grep -v '^#' | sed 's/#.\+//' | perl -lane 'printf "%s=%s:", shift @F, join ";", @F;'
      $ chmod 0755 ~/bin/string2lscolors
      

      Awesome, now you can call the command 'string2lscolors' anywhere you want.

      To try it out, do the above steps, and you can download my example human legible format file, and try it out..

      $ wget http://leocharre.com/docs/lscolorscheme.txt -O /tmp/lscolorscheme.txt
      $ cat /tmp/lscolorscheme.txt | string2lscolors
      no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=00;91:*.cmd=00;32:*.exe=00;32:*.gz=00;90:*.bz2=00;90:*.bz=00;90:*.tz=00;90:*.rpm=00;90:*.rar=00;90:*.zip=00;90:*.iso=00;90:*.cpio=00;31:*.c=33:*.h=33:*.sh=33:*.t=33:*.pm=33:*.pl=33:*.cgi=33:*.pod=33:*.PL=33:*.js=33:*.php=33:*.off=00;9:*.bak=00;9:*.old=00;9:*.htm=94:*.html=94:*.txt=94:*.text=94:*.css=94:*.avi=96:*.wmv=96:*.mpeg=96:*.mpg=96:*.mov=96:*.AVI=96:*.WMV=96:*.mkv=96:*.jpg=96:*.jpeg=96:*.png=96:*.xcf=96:*.JPG=96:*.gif=96:*.svg=96:*.eps=00;96:*.pdf=00;96:*.PDF=00;96:*.ps=00;96:*.ai=00;91:*.doc=00;91:*.csv=95:*.dsv=95:*.db=95:*.sql=95:*.meta=95:*.xml=95:*.yaml=95:*.yml=95:*.conf=95:
      
      c) You can make sure the thing is presented kind of decent, writing a minimal program..
      This means a few things.. One for damn sure, that if I ask for --help, I get help. There are no conditions about this. It simply has to be there. If you can't assure your programs at least have the appropriate help docs- you should not be writing software, unless you're using microsoft type environments, then- knock yourself out- You won't be heard from again.

      The reason is that in any sort of interface- you must present your users with what they are already familiar with. This is explained in The Rule of Least Surprise. And by the way- those rules there, the basics of the unix philosophy.. Those are not little poems- those the FUCKING RULES, maggot! It's what separates the women from the little girls. You don't have to always abide by all of them- but you must aspire to at *least* make note of most of them.

      If you're having any doubts about the supremacy of unix philosophy- please, read those rules, carefully, slowly. If you're still in doubt, consider that programs written in unix shell ten or twenty years ago, still work today.

      This is how you would write the same one liner up there, as a more useful program.. Again, this is minimal, but you can see that instead of two lines we have 117.. See string2LS_COLORS.sh.

    2. Using the filter to automatically edit your ~/.bashrc..

      Soon...

My personal LS_COLOR environment variable for perl development- I have refined mine to be:

LS_COLORS='no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=00;35:*.cmd=00;32:*.exe=00;32:*.sh=00;32:*.gz=00;31:*.bz2=00;31:*.bz=00;31:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.t=93:*.pm=00;36:*.pod=00;96:*.conf=00;33:*.off=00;9:*.jpg=00;94:*.png=00;94:*.xcf=00;94:*.JPG=00;94:*.gif=00;94:*.pdf=00;91'

This helps me detect some things that are important to me, such as tests.t to stand out from other junk in t/, a different color for pod and pm files.. etc. very cool.

I've been using this across my servers. And it does help. It's a very small thing to do, to improve your development environment. I suggest you try it out.


Linux User