ls content of a directory ignoring symlinks

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.

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.

Answered By: tijagi

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

Answered By: Thushi

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.

Answered By: Michael Homer

Try this one:

ls | grep -v " -> "
Answered By: csny

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.

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=nnHERE_THERE_BE_A_LINK>>:' 
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=nnHERE_THERE_BE_A_LINK>>:' 
ls -1 --color=always | sed -n l
1file$
$
$
00HERE_THERE_BE_A_LINK>>001filelink@$
2file$
$
$
00HERE_THERE_BE_A_LINK>>002filelink@$
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:

 33 - 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 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:

  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=//: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 -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.

Answered By: mikeserv

Or, simpler:

ls -l | grep -v ^l

Explanation

ls -l means to list in long form. When you do this, the first string of characters gives information about each file. The first character indicates what type each file is. If it’s a symlink, then an l is the first character.

grep -v ^l means to filter out (-v) the lines that start with (^) an l.

Answered By: Jonathan Petitcolas

The answer above lead to the following:

 ls -1F | grep -i "/"

Oddly enough leaving the 1 out of the ls command still gives a list.

Answered By: woodsjay
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.