You can use $LS_COLORS
to do this. If your version of ls
supports specifying the colors using that variable, you can define output per file type. It's builtin behavior and very configurable. So I created some files to demo this like:
for f in 9 8 7 6 5 4 3 2 1
do touch "${f}file" &&
ln -s ./"${f}file" ./"${f}filelink"
done
So now I'll do:
LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \
ls -1 --color=always | cat
###OUTPUT###
1file
HERE_THERE_BE_A_LINK>>1filelink@
2file
HERE_THERE_BE_A_LINK>>2filelink@
3file
...
HERE_THERE_BE_A_LINK>>8filelink@
9file
...
And the nulls are there too...
LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \
ls -1 --color=always | sed -n l
1file$
$
$
\000HERE_THERE_BE_A_LINK>>\0001filelink@$
2file$
$
$
\000HERE_THERE_BE_A_LINK>>\0002filelink@$
3file$
...
You can specify for all or any filetypes. Doing so for only a single filetype might not get you want though as ls
has incorporated some defaulted compilation values for terminal escapes. You'd do much better to address the api as a single interface. Here's a simple little means of parsing and assigning current environment dircolors
configured defaults:
LS_COLORS='rs=:no=//:lc=:rc=:ec=//:'$(
set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=/$fc//:"
done) ls -l --color=always | cat
Its output in my home directory looks like this:
total 884
///-rw-r--r-- 1 mikeserv mikeserv 793 Jul 9 11:23 /fi//1/
//drwxr-xr-x 1 mikeserv mikeserv 574 Jun 24 16:50 /di//Desktop//
//-rw-r--r-- 1 mikeserv mikeserv 166 Jul 4 23:02 /fi//Terminology.log/
//-rw-r--r-- 1 mikeserv mikeserv 0 Jul 6 11:24 /fi//new
file/
//lrwxrwxrwx 1 mikeserv mikeserv 10 Jul 11 04:18 /ln//new
file
link/ -> /fi//./new
file/
//-rwxr-xr-x 1 mikeserv mikeserv 190 Jun 22 11:26 /ex//script.sh/*
//-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 /fi//shot-2014-06-22_17-10-16.jpg/
//-rw-r--r-- 1 mikeserv mikeserv 68 Jun 17 19:59 /fi//target.txt/
You can run that with cat -A
too and the only difference you'll encounter is that you'll see $
for newlines - there are no unprintable characters introduced by ls --color=always
with this configuration - only what you see here.
ls
inserts its default terminal escapes like this:
${lc}${type_code}${rc}FILENAME${lc}${rs}${rc}
...where the default values for $lc
(left of code), $rc
(right of code), and $rs
(reset) are:
\033 - ESCAPE
m - END ESCAPE
0 - reset
...respectively. ${type_code}
is used to stand in for the various fi
(regular file - default unset), di
(directory), ln
(link), and every other file type I know of. There is also $no
(normal) which is also by default unset and which is here represented by the //
at the beginning of each line. My simple little IFS=:
block works just by inserting the name for each configurable in also as its own value and adding a slash or two - though \0
NUL bytes would do as well.
By default ls
will also insert one $rs
immediately preceding its first output $lc
- but this is not accurately represented here. In this case I have specified $ec
(end code) which stands in for $rs
in all cases - when it is specified you don't get an extra $rs
between $no
and ${type_code}
as you would otherwise - it only presents immediately following a filename and once at the start of output - as you can see in the one extra slash at the head of line one.
Here's a snippet from my own $LS_COLORS
printf %s "$LS_COLORS"
rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:\
so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:\
or=40;31;01:su=37;41:sg=30;43:ca=30;41:\
tw=30;42:ow=34;42:st=37;44:ex=01;32:\
*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:...
And, in truth, my little shell hack is probably overly complicated - there's a widely available interface for assigning these values. Try dircolors -p
in your cli and info dircolors
for more information on that.
You can wrap the filenames in arbitrary strings. You can comment them out if you wish. You can specify similar behaviors based only on file extension. There's really not a lot you can't specify that way.
Now I'm not just making all of this up either - I learned about it after stumbling on the source code by accident.
With this particular configuration ls
will emit:
$no
- once per record at the start of each record
${type_code}
- once immediately preceding each filename to include an abbreviation of the file's type and always occurring on the same line as and 7 whitespace delimited fields after $no
or immediately following a ->
denoting a symbolic link's target.
$ec
- once immediately preceding the very first line and thereafter only once immediately following each filename.
All other values are empty.
What follows is a null-delimited ls
, and this time I will use cat -A
, though, without it, it would look the same as the last example :
LS_COLORS='rs=:no=\0//:lc=:rc=:ec=\0//:'$(
set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=/$fc//\0:"
done) ls -l --color=always | cat -A
total 884$
^@//^@//-rw-r--r-- 1 mikeserv mikeserv 793 Jul 9 11:23 /fi//^@1^@//$
^@//drwxr-xr-x 1 mikeserv mikeserv 574 Jun 24 16:50 /di//^@Desktop^@///$
^@//-rw-r--r-- 1 mikeserv mikeserv 166 Jul 4 23:02 /fi//^@Terminology.log^@//$
^@//-rw-r--r-- 1 mikeserv mikeserv 0 Jul 6 11:24 /fi//^@new$
file^@//$
^@//lrwxrwxrwx 1 mikeserv mikeserv 10 Jul 11 04:18 /ln//^@new$
file$
link^@// -> /fi//^@./new$
file^@//$
^@//-rwxr-xr-x 1 mikeserv mikeserv 190 Jun 22 11:26 /ex//^@script.sh^@//*$
^@//-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 /fi//^@shot-2014-06-22_17-10-16.jpg^@//$
^@//-rw-r--r-- 1 mikeserv mikeserv 68 Jun 17 19:59 /fi//^@target.txt^@//$
And so to reliably remove all symbolic links from a -l
ong listing like this one, you might make a simple change:
LS_COLORS='rs=:no=//:lc=:rc=:ec=/ :'$(
set -- di fi mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=$fc/:"
done)ln=///: ls -l --color=always | sed ':ln
\|///|{N;\|\n//|!bln};s|.*//||'
My results after running that look like...
total 884
-rw-r--r-- 1 mikeserv mikeserv 793 Jul 9 11:23 fi/1/
drwxr-xr-x 1 mikeserv mikeserv 574 Jun 24 16:50 di/Desktop/ /
-rw-r--r-- 1 mikeserv mikeserv 166 Jul 4 23:02 fi/Terminology.log/
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 6 11:24 fi/new
file/
-rwxr-xr-x 1 mikeserv mikeserv 190 Jun 22 11:26 ex/script.sh/ *
-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 fi/shot-2014-06-22_17-10-16.jpg/
-rw-r--r-- 1 mikeserv mikeserv 68 Jun 17 19:59 fi/target.txt/
Using some command like to the one I do above:
LSCOLORS=...$(...)fc1=///:fc2=///: ls ... | sed ...
...(where fc1
and fc2
are filetypes listed after set --
in the subshell) should serve to reliably remove any combinations of filetypes you could want from ls
output regardless of any characters the filenames might contain.
grep
for (see last paragraph). – Michael Homer Jul 09 '14 at 08:57grep -H
, if you have it (seeman grep
), or otherwisegrep 'pat' '{}' /dev/null ';'
to trick it into thinking there are multiple files if you don't. – Michael Homer Jul 09 '14 at 09:13find ... -exec grep 'pattern' '{}' \+
-- using a plus instead of a semicolon tellsfind
to pass as many files as possible to thegrep
, rather than limiting it to one file at a time. This will also be much more efficient, spawning fewergrep
processes. – evilsoup Jul 09 '14 at 12:29+
fails to meet that requirement in the degenerate case where there is only one file or one file left at the end. – Michael Homer Jul 09 '14 at 20:10