Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I have a simple alias to list the size of files&folders in cwd.

( including dotfiles in this case , ignoring zero sizes )

du -sh .[!.]* * | sort -hr | grep -v '^0'

which can be achieved aswell with find:

find .[!.]* * -maxdepth 0 -exec du -sh {} \; | sort -hr | grep -v '^0'

example output:

// .ssh & .byobu are folders - .zsh* are files
// currently not able to distinguish them by type

... 
32K     .zshrc
25K     .ssh
20K     .zcompdump
19K     .byobu
...

How can i color the files/directories in the output matching the ls colors? ( LS_COLORS )

share|improve this question
    
I can't see why this doesn't work - it does with a print, but gets mangled with the system: du -sh .[!.]* * | sort -hr | grep -v '^0' | awk '{print $1;system( "ls -d --color '$2'") }' –  Paul Jun 8 '13 at 22:40
    
this definitely points to the right direction... The output is colored ( all with ls's dir color ) ... but all files/folders are transformed to "/n." . thanks for your efforts - i realize the solution will probably be some tricky sed/awk !! :) –  nifr Jun 8 '13 at 23:08
    
you can also use the --color option available for grep .... eg : ` find . -iname "t*" | du -sh * | grep stuff --color=auto` –  nsd Jun 8 '13 at 23:22
    
Thanks - i already have grep aliased to grep --color=auto ... grep will highlight a search string/pattern in the output ... but not divide between files & folders in the output using the LS_COLORS. Thanks for the hint anyways :) –  nifr Jun 8 '13 at 23:25

2 Answers 2

up vote 1 down vote accepted

This zsh script parses $LS_COLORS. It needs only a stat call on every file and hence is much faster than the solution at the bottom, which calls ls for every file. And it handles files with spaces correctly. (\n or \t are still not allowed in file names)

However, the implementation is not complete. I only included colors for different filetypes which can be identified by the first character of the file mode string (e. g. lrwxrwxrwx for a symbolic link) or by the file extension. That means, that world-writable permissions, suid or sticky bits are not colored specially. To include those also should be straight forward.

source the following and use the new shell function duc for a colored du output:

zmodload -F zsh/stat b:zstat

function duc() {

  emulate zsh 
  setopt no_nomatch interactivecomments           # no_nomatch is necessary, to prevent error in "do .* *" if there are no dotfiles

  typeset -a aline
  typeset -A lscols
  local dircols acolor

  for i (${(s.:.)LS_COLORS}) {                    # split $LS_COLORS at ":"
    aline=(${(s:=:)i})                            # split every entry at "="
    lscols+=(${${aline[1]}/*.} ${aline[2]})       # load every entry into the associative array $lscols
  }

  duout=$(du -sh .* * 2> /dev/null | grep -v '^0' | sort -hr)
  for i (${(f)duout}) {                           # split output of "du" at newlines
    aline=(${(ps:\t:)i})                          # split every entry at \t
    zstat -s +mode -A atype ${aline[2]}           # determine mode (e.g. "drwx------") of file ${aline[2]}
    case ${${atype[1]}[1]} in                     # ${${atype[1]}[1]} is the first character of the file mode
      b)   acolor=$lscols[bd] ;;
      c|C) acolor=$lscols[cd] ;;
      d)   acolor=$lscols[di] ;;
      l)   acolor=$lscols[ln] ;;
      p)   acolor=$lscols[pi] ;;
      s)   acolor=$lscols[so] ;;
      -)   acolor=${lscols[${${aline[2]}:e}]};      # ${${aline[2]}:e} is the current file extention
           [[ -z $acolor ]] && acolor=$lscols[fi]   # unrecognized extention
           [[ -z $acolor ]] && acolor=00            # sometimes "fi" isn't set in $LS_COLORS, so fall back to normal color
           ;;
      *)   acolor=00 ;;
    esac
    print -n -- "${aline[1]}\t"        # print size (taken from du output)
    print -n "\\e[4${acolor}m"         # activate color
    print -n ${aline[2]}               # print file name
    print "\\e[0m"                     # deactivate color
  }
}

This is my old script, also for zsh. It probably unnecessarily complex and is really slow, as for every file a single ls command is issued:

du_colored() {
  typeset -a duout
  duout=($(du -sh .* * | sort -hr | grep -v '^0'))
  for i ({1..$#duout..2}) {
    print -n "${duout[$i]}\t"
    ls -d --color ${duout[$(($i+1))]}
  }
}
  • the .* in zsh will not match . or .., but files like ..foo which will be missed with .[!.]*
  • typeset -a declares an array
  • the for loops aver the array, $i takes values from 1 onwards in steps of 2

Warning: This will break badly when there are files with blanks... I don't have a better idea at the moment.

share|improve this answer
    
+1 for this being the first almost working solution. executing ls for every file seems overkill. simply matching one ls -a --colors ( outputting the colors ) against the second string ( folder/filename without color ) in the output of du would be more performant. trying to achieve it but can't find a sufficient way yet :/ –  nifr Jun 8 '13 at 23:32
    
ls -a --color | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" removes the colors - now how can we replace the du output with the colored output of ls -a --color ? :) –  nifr Jun 8 '13 at 23:38
    
@nifr: I added another script, that parses $LS_COLORS directly. This runs much faster than my yesterday's long-after-midnight hack as it only needs to stat every file instead of running ls. –  mpy Jun 9 '13 at 13:10
    
Nice addition thank you - i hacked together a solution for bash yesterday in a night session - will post it below, maybe you can review it :) –  nifr Jun 10 '13 at 0:29

This is what i came up with for bash - uses pv to show the output progress

folder_size (){
  # read ls --color output into ls_colored_array 
  # thereby remove symlinks (@) and remove (/ and *) from folders and files

  ls -AF --color | grep -v @ | sed s'/[/\,*]$//'| xargs -n1 -L1 | read -d '\n' -r -a ls_colored_array

  # - loop over the array and issue du -sh for every element
  # - exchange du's ouput with ls's 
  # - run loop through pv with line option and
  #    size set to array-length showing progress
  # - finally sort the output using sort

  for i in "${ls_colored_array[@]}"; do
    echo -n "${i}" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | xargs -n1 -0 du -sh | awk -v i="$i" '{ printf "%-10s ", $1; $1=""; print i }'
  done | pv -pls"${#ls_colored_array[@]}" | sort -hr
}

Script can now be turned into a one-liner ... but i'll improve the function by adding flags for i.e. 10 biggest files/folders or folders only.

share|improve this answer
    
This is a nice solution, especially that part with the progress bar, +1 for that :) However, I had to change the read line to read -d '\n' -r -a ls_colored_array <<< $(ls -AF --color | grep -v @ | sed s'/[/\,*]$//'| xargs -n1 -L1) to get it working in plain bash (related: superuser.com/questions/173337/…). Unfortunately this script breaks on blanks in file names. (I suppose you need to use a counter to access all the $ls_colored_array elements instead of passing the array directly to the for loop). –  mpy Jun 10 '13 at 8:45

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.