Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a client with a directory tree that has a number of high-level directories that I need to have on my local development machine. I rarely look at them unless something goes awry.

I've marked the "uninteresting" ones with the grey color label and the ones I use daily with a green color label in Finder. That works fine on the rare occasion when I'm using Finder.

Instead, I use Terminal.app, although I am not against other terminal emulators as long as they are fast and robust. When using ls, even with the @ and G flags, I don't have useful information that I can use to de-emphasize the uninteresting directory entries. (If I could pipe ls through a simple awk script to apply coloring, I'd be fine with that.)

I am aware that I can use osascript to get this attribute and then decorate the file output, but that will probably result in the slowest ls implementation since Unicos. I also know I can change the default colors with ls, but that doesn't quite dig to the level that I need.

Is there a simple, fast, tool that already exists that will color ls output based on Finder label colors, and then fall back to $LSCOLORS? Or is that my next github project? :-)

Thanks!

share|improve this question
    
It's actually quite easy to compile a modified ls as I explain here. It should be possible to replace the color by type with color by attribute. – Daniel Beck Dec 6 '12 at 6:02
up vote 14 down vote accepted

Color information is available from the com.apple.FinderInfo extended attribute.

$ xattr -p com.apple.FinderInfo filename
00 00 00 00 00 00 00 00 00 0C 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

First row, tenth byte, bits 3-1 (i.e. those with binary values 2, 4 and 8). The example output is for a red file with no other values set.

If the attribute isn't available or the nibble is 0, it's colorless.

Possible values in context-menu order from left to right: 0 (colorless), C (red), E, A, 4, 8, 6, 2 (gray).

Remember to check for possible 1 values for that last bit, i.e. 3 would also be gray + some other attribute.


One possible solution would be the following:

Define a function ls in ~/.bash_profile that does something slightly different if no parameters are given:

function ls {
        if [ "$#" = "0" ] ; then
                find . -maxdepth 1 -exec ~/Library/Scripts/colorize.sh '{}' \;
        else
                $( which ls ) $@
        fi
}       

colorize.sh could look like the following, calling a Python script and printing the filename depending on its output:

#!/bin/bash

filename=$1

if [ ! -e "$filename" ] ; then
    exit
fi

filename="$( basename "$filename" )"

attrs=( $( $( dirname $0 )/attrs.py "$filename" | cut -f2 ) )

hidden=${attrs[0]}

if [ "$hidden" = "True" ] ; then
    exit
fi

color=${attrs[1]}

case "$color" in
    "none" )
        format="setab 8"
        ;;
    "red" )
        format="setab 1"
        ;;
    "orange" )
        format="setab 1"
        ;;
    "yellow" )
        format="setab 3"
        ;;
    "green" )
        format="setab 2"
        ;;
    "blue" )
        format="setab 4"
        ;;
    "purple" )
        format="setab 5"
        ;;
    "gray" )
        format="setab 7"
        ;;
esac

echo "$( tput $format )$filename$( tput sgr0 )"

And attrs.py, which extracts relevant file attributes, in the same directory:

#!/usr/bin/env python

from sys import argv, exit
from xattr import xattr
from struct import unpack

if len(argv) < 2:
    print('Missing filename argument!')
    exit(1)

attrs = xattr(argv[1])

try:
    finder_attrs= attrs[u'com.apple.FinderInfo']

    flags = unpack(32*'B', finder_attrs)

    hidden = flags[8] & 64 == 64
    color = flags[9] >> 1 & 7
except:
    hidden = False
    color = 0

colornames = { 0: 'none', 1: 'gray', 2 : 'green', 3 : 'purple', 4 : 'blue', 5 : 'yellow', 6 : 'red', 7 : 'orange' }

print 'hidden:\t', hidden
print 'color:\t', colornames[color]

I didn't have a large enough matching color selection here, so red and orange are both printed in red.

enter image description here

enter image description here

I added the hidden attribute here since I am interested in that part of modifying ls output. Just remove the if statement if you don't want it.

share|improve this answer
    
Thanks, that's a great answer and I think it puts me further on the road to what I need to accomplish. – Art Taylor Jun 10 '11 at 17:02

Thanks to @DanielBeck for his detailed answer.

Here is a very quick and dirty solution, useful to quickly check the label of some file on ssh:

#!/bin/bash

color_08=$'\e[44;30m' #blue
color_02=$'\e[47;30m' #gray
color_04=$'\e[42;30m' #green
color_0E=$'\e[46;30m' #orange/cyan
color_06=$'\e[45;30m' #purple
color_0C=$'\e[41;30m' #red
color_0A=$'\e[43;30m' #yellow
color_00=$'\e[m'
end=$'\e[K\e[m'

for f in *; do
    x=$(xattr -p com.apple.FinderInfo "$f" 2>/dev/null)
    x=${x:27:2}
    x=color_${x:-00}
    echo "${!x}$f$end"
done

Warning: do not use this in any professional shape or form, nor use it on any file provided by somebody else, because it uses shell indirection, which is just as bad as eval.

Also, the colors were chosen for my specific color scheme (Solarized Light). You may need to adjust the escape sequences to suit your scheme.

Here's a test folder:

test foldel

And here's how it looks with my script:

script output

Orange becomes cyan, because that's what the terminal provides.

And yes, it's 2016 and they will have to pry OS X 10.8 from my cold, dead fingers :-)

share|improve this answer

You must log in to answer this question.

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