0

I am currently working on an issue using grep and ls. The goal is to list all lines that are link files in my current directory. I have 3 link files in my directory, but when I execute my command it only shows one.

Here is what I'm typing:

ls -lR | grep l*

Any help would be appreciated. Thanks

fra-san
  • 10,205
  • 2
  • 22
  • 43

3 Answers3

1

Your pipeline has a few issues.

  1. Parsing the output of ls is not a good idea (see also Why *not* parse `ls` (and what to do instead)?). Its format is not standardized and file names can contain any combination of bytes except for NUL and /. Hence, it doesn't guarantee to list file names in an unambiguous form. The frequently given example involves newline characters (note how invoking ls in different ways may lead to distinct, possibly ambiguous, results):

    $ touch 'foo
    > bar'
    $ ls -1
    'foo'$'\n''bar'
    $ ls | cat
    foo
    bar
    

    Are foo and bar two distinct files or two parts of the same file name?

    The safe way to recursively search a directory for files of a given type is find.

  2. The unescaped string l* is interpreted by the shell as a globbing expression. It is expanded to the list of file names (by a broad definition of "file") that start with l in the current directory.

    # In a directory you created for testing purposes
    $ rm *; touch foo; ln -s foo link1; ln -s foo link2; ln -s foo link3
    $ set -x; ls -lR | grep l*
    + ls -lR
    + grep link1 link2 link3
    

    The xtrace shell option (i.e. set -x, available in several shells and specified by POSIX) is useful to show the effect of filename expansion (globbing): l* expands to link1 link2 link3 and grep is therefore searching for the string link1 in the content of foo (through the symbolic links link2 and link3). (grep doesn't read standard input when it is given a list of file names after the pattern to search).

    If you happen to have just one file whose name starts with l:

    # In a directory you created for testing purposes
    $ rm *; touch foo; ln -s foo link1; ln -s foo Link2; ln -s foo Link3
    $ set -x; ls -lR | grep l*
    + ls -lR
    + grep link1
    lrwxrwxrwx 1 user group 3 May  1 03:04 link1 -> foo
    

    Then grep searches its standard input (the output of ls) for the string link1, here matching one line. (Reading standard input is what grep does when it is given no file names, or if a file-name argument is -).

    You should quote literal strings meant to be passed as arguments to prevent the shell from performing filename expansion. As an alternative, where supported by your shell, filename expansion can be disabled using set -f.

  3. If no file name in the current directory starts with l, the globbing expression is left untouched and passed to grep as the first positional argument. grep will then interpret it as a regular expression (defaulting to the BRE variant) which reads "for any line of input, match an optional l (anywhere)", effectively matching anything:

    # In a directory you created for testing purposes
    $ rm *; touch foo
    $ ls -lR
    .:
    total 0
    -rw-r--r-- 1 user group 0 May  1 02:49 foo
    $ set -x; ls -lR | grep l*
    + ls -lR
    + grep 'l*'
    .:
    total 0
    -rw-r--r-- 1 user group 0 May  1 03:14 foo
    

    To match a string that starts with a literal l you need an anchored expression: grep '^l'.

fra-san
  • 10,205
  • 2
  • 22
  • 43
1

@NasirRiley is on the right track. To list the symlinks in the current directory run find . -mindepth 1 -maxdepth 1 -type l. Test:

$ cd "$(mktemp --directory)"
$ mkdir foo
$ touch foo/bar baz
$ ln --symbolic foo/bar ban
$ ln --symbolic bar foo/bat # symlink in subdirectory, should be ignored
$ find . -mindepth 1 -maxdepth 1 -type l
./ban
l0b0
  • 51,350
  • As he is using ls -lR, I figured that he wanted to search through the subdirectories as well. If he doesn't, then your answer is accurate. – Nasir Riley May 01 '20 at 11:45
0

Just so you know, the command you were looking for is:

ls -l | grep '^l'

The quotes aren't necessarily needed.

The removal of the -R option on ls is because you specified that you wanted the current directory, not the directory tree.

And either way, the answers pointing you to find are probably a better solution.

David G.
  • 1,369