Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a directory in which I would like to list all the content (files and sub directories) without showing the symbolic links. I am using GNU utilities on Linux. The ls version is 8.13.

Example:

Full directory listing:

~/test$ ls -Gg
total 12
drwxrwxr-x 2 4096 Jul  9 10:29 dir1
drwxrwxr-x 2 4096 Jul  9 10:29 dir2
drwxrwxr-x 2 4096 Jul  9 10:29 dir3
-rw-rw-r-- 1    0 Jul  9 10:29 file1
-rw-rw-r-- 1    0 Jul  9 10:29 file2
lrwxrwxrwx 1    5 Jul  9 10:29 link1 -> link1
lrwxrwxrwx 1    5 Jul  9 10:30 link2 -> link2

What I would like to get

~/test$ ls -somthing (or bash hack)
total 12
dir1 dir2 dir3 file1 file2

NOTE: My main motivation is to do a recursive grep (GNU grep 2.10) without following symlinks.

share|improve this question

6 Answers 6

up vote 7 down vote accepted

For the stated question you can use find:

find . -mindepth 1 ! -type l

will list all files and directories in the current directory or any subdirectories that are not symlinks.

mindepth 1 is just to skip the . current-directory entry. The meat of it is the combination of -type l, which means "is a symbolic link", and !, which means negate the following test. In combination they match every file that is not a symlink. This lists all files and directories recursively, but no symlinks.

If you just want regular files (and not directories):

find . -type f

To include only the direct children of this directory, and not all others recursively:

find . -mindepth 1 -maxdepth 1

You can combine those (and other) tests together to get the list of files you want.

To execute a particular grep on every file matching the tests you're using, use -exec:

find . -type f -exec grep -H 'some pattern' '{}' +

The '{}' will be replaced with the files. The + is necessary to tell find your command is done. The option -H forces grep to display a file name even if it happens to run with a single matching file.

share|improve this answer
    
It doesn't show the files in witch the pattern is found. –  Alan Jul 9 at 8:56
    
That's what you run grep for (see last paragraph). –  Michael Homer Jul 9 at 8:57
    
I doesn't work for me. It just outputs the the found patterns without a filename where it was found. –  Alan Jul 9 at 9:09
1  
Use grep -H, if you have it (see man grep), or otherwise grep 'pat' '{}' /dev/null ';' to trick it into thinking there are multiple files if you don't. –  Michael Homer Jul 9 at 9:13
1  
+ 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 9 at 20:10

From version 2.12 onwards, the -r option for GNU grep doesn’t dereference symbolic links unless you specify them by hand:

-r, --recursive

Read all files under each directory, recursively, following symbolic links only if they are on the command line. This is equivalent to the -d recurse option.

-R, --dereference-recursive

Read all files under each directory, recursively. Follow all symbolic links, unlike -r.

share|improve this answer
    
What version of grep is that. I have -R, -r, --recursive Read all files under each directory, recursively; this is equivalent to the -d recurse option. in 2.10 –  Alan Jul 9 at 8:45
    
grep (GNU grep) 2.14 –  tijagi Jul 9 at 10:16

In zsh, this would be easy thanks to glob qualifiers:

grep PATTERN **/*(.)

The pattern **/ traverses subdirectories recursively. The glob qualifier . restricts matching to regular files.

Without zsh, use find (see Michael Horner's answer). And in this particular case, GNU grep can do what you want (it's exactly what grep -r does) — but only since version 2.12, earlier versions did follow symbolic links.

share|improve this answer

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 \0NUL 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:

  1. $no - once per record at the start of each record

  2. ${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.

  3. $ec - once immediately preceding the very first line and thereafter only once immediately following each filename.

  4. 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 -long 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.

share|improve this answer
    
Heh, nice, +1 for originality. Seems to choke on directories though, they're printed with a garbled ANSI color code. Anyway, could you add an explanation of how this works? Perhaps explain what lc, nc, etc are? –  terdon Jul 11 at 9:40
    
@terdon - They're only printed with the ANSI escapes already set in your environment and or compiled in as a default for ls's di= value. You'd need to add di=: to the var to nullify that - and for all others. It's what I mean by the api - you address each filetype but you have to address the interface the same in all cases. So setting ln one way and ignoring di defaults won't work out. There are many defaults compiled in as well... Hmm. Your asking about lc and nc and stuff is valid - completely - I already did it the other day though. I'll link it, I think. –  mikeserv Jul 11 at 9:57
    
Ouch, so I'd have to either specify all possible file types always or check what's present before hand? OK, thanks. –  terdon Jul 11 at 10:00
    
@terdon - see my edit just now - it does it all in a couple lines of shell, specifying each type by its type name and reliably delimiting every file name. There are no nonprintables introduced either. And there's an api for that anyway - it's dircolors. It reads a config file and outputs a shell eval friendly var value. It's pretty easy - but so are the few lines up there. Anyway, if you wanted to use dircolors you just save two different files and invoke it that way. Also, I showed you the other day how to do both at once with \0NUL delimiters as part of each escape sequence. –  mikeserv Jul 11 at 10:25
    
@terdon - I tried to make it clearer how easy it is to set all of the values that might be compiled in - I think I got em all, maybe too many... 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 –  mikeserv Jul 11 at 12:20

Try this command: ls -p | grep -v @

share|improve this answer
    
Nope, lists the links. –  Alan Jul 9 at 8:47
    
@Alan: k try this find -type f –  Thushi Jul 9 at 8:58
1  
-1 because: i) The -p just adds a slash to directories, you're looking for -F ii) your find alternative will not list directories and iii) Please test your answers before posting. –  terdon Jul 9 at 11:29

Try this one:

ls | grep -v " -> "
share|improve this answer
2  
That will fail for various reasons, for example on a file called a -> b. It will also fail on file names with newlines and other strangeness. Please read this for why parsing ls is a bad idea. –  terdon Jul 9 at 11:30
    
@terdon There are more fundamental reasons why this will fail. Either ls is aliased to ls -l, and this returns columns before the file name, or it isn't, and this doesn't do anything special about symbolic links. –  Gilles Jul 10 at 21:01
    
@terdon - ls parses itself. we all parse ls every time we print to a terminal - there's an api. I posted an answer demonstrating something like it here - but you can do a lot more. I showed you just the other day - you had null-delimited filenames yourself. –  mikeserv Jul 10 at 21:51
    
@mikeserv I know, I know. However, explaining how to do this safely takes several hundred lines as you yourself have demonstrated. As it stands, this answer suffers from all the dangers of parsing ls and the OP is clearly not aware of them. –  terdon Jul 11 at 9:30
    
@terdon - ls isn't going to eat your children or anything - it's only a program. If you don't explain it then it will take several hundred to explain it and several hundred more to explain why the several hundred lines you just linked to are wrong. And besides - this is only two lines: LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \ ls -1 --color=always | cat –  mikeserv Jul 11 at 11:24

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.