15

I can show the target file that a link points to using ls -l:

snowch$ ls -l /usr/local/bin/mvn
lrwxr-xr-x  1 snowch  admin  29 12 Dec 08:58 /usr/local/bin/mvn -> ../Cellar/maven/3.2.3/bin/mvn

Is there a way to show less output without having to pipe through another command such as awk? E.g:

snowch$ ls ?? /usr/local/bin/mvn
/usr/local/bin/mvn -> ../Cellar/maven/3.2.3/bin/mvn

I'm running 3.2.53 on OS X 10.9.5. The output from several commands is shown below:

snowch$ ls -H /usr/local/bin/mvn
/usr/local/bin/mvn

snowch$ ls -L /usr/local/bin/mvn
/usr/local/bin/mvn

snowch$ file /usr/local/bin/mvn
/usr/local/bin/mvn: POSIX shell script text executable

snowch$ file -b /usr/local/bin/mvn
POSIX shell script text executable

4 Answers 4

9

ls unfortunately doesn't have an option to retrieve file attributes and display them in an arbitrary way. Some systems have separate commands for that (for instance GNU has a stat command or the functionality in GNU find).

On most modern systems, with most files, this should work though:

$ ln -s '/foo/bar -> baz' the-file
$ LC_ALL=C ls -ldn the-file | sed '
   1s/^\([^[:blank:]]\{1,\}[[:blank:]]\{1,\}\)\{8\}//'
the-file -> /foo/bar -> baz

That works by removing the first 8 blank delimited fields of the first line of the output of ls -l. That should work except on systems where the gid is not displayed there or the first 2 fields are joined together when there's a large number of links.

With GNU stat:

$ LC_ALL=C stat -c '%N' the-file
'the-file' -> '/foo/bar -> baz'

With GNU find:

$ find the-file -prune \( -type l -printf '%p -> %l\n' -o -printf '%p\n' \)
the-file -> /foo/bar -> baz

With FreeBSD/OS/X stat:

f=the-file
if [ -L "$f" ]; then
  stat -f "%N -> %Y" -- "$f"
else
  printf '%s\n' "$f"
fi

With zsh stat:

zmodload zsh/stat
f=the-file
zstat -LH s -- "$f"
printf '%s\n' ${s[link]:-$f}

Many systems also have a readlink command to specifically get the target of a link:

f=the-file
if [ -L "$f" ]; then
  printf '%s -> ' "$f"
  readlink -- "$f"
else
  printf '%s\n' "$f"
fi
3
  • stat -f "%N -> %Y" -- /usr/local/bin/mvn worked great. Thanks!
    – Chris Snow
    Commented Dec 12, 2014 at 10:51
  • 1
    On an old RHEL6 system, stat is of GNU coreutils 8.4 and there it's a stat -c %N -- /usr/local/bin/mvn. To remove the quotations I had to pipe this into | perl -pe 's/['"'"'`]//g'
    – cfi
    Commented Aug 31, 2016 at 9:00
  • @cfi tr -d \'\` would be enough. Note that that RHEL system would have GNU find with which you'll have more control over. Commented Aug 31, 2016 at 9:26
6

Use the file command.

[sreeraj@server ~]$ ls -l mytest
lrwxrwxrwx 1 sreeraj sreeraj 15 Dec 12 09:31 mytest -> /usr/sbin/httpd

[sreeraj@server ~]$ file mytest
mytest: symbolic link to `/usr/sbin/httpd'

or

[sreeraj@server ~]$ file -b mytest
symbolic link to `/usr/sbin/httpd'
[sreeraj@server ~]$

Also, please go read through man page of ls and check the options -L and -H and see if that would suffice your requirement.

3
  • @Sree - Many thanks! I had tried the ls options, but not file. Unfortunately, neither seems to work :(
    – Chris Snow
    Commented Dec 12, 2014 at 10:04
  • @SHC Sorry, I assumed that you are on a linux box. Please add osx as one of your tags. That should get to the attention of osx users as well. I have edited the post to add the tag, but I don't have enough credits for the edit to appear immediately.
    – Sreeraj
    Commented Dec 12, 2014 at 10:08
  • maybe check 'readlink' utility on osx.
    – tonioc
    Commented Dec 12, 2014 at 10:35
2

With a GNU ls at least (and, apparently, tcsh's implementation) you can hack the $LS_COLORS environment variable to insert delimiters where you like (but tcsh's builtin ls-F doesn't do link targets - only link flags) Usually ls inserts arbitrary non-printable terminal escapes based on the values stored within that environment var, but there's nothing stopping us from inserting arbitrary anything else instead. More on this here.

For example:

LS_COLORS='ln=///\n:lc=:no=//:rc=:rs=:' \
\ls ~ -l --color=always | 
sed '\|///|,\|//|!d;//d'

That puts a string like // at the head of every listing (so just before lrwcrwx) and a ///\n just before the filename of any link. sed then filters on line ranges - it will delete every input line until it encounters /// and from there through the next line which matches // it will delete lines matching //. So it only gets the link name and link target - regardless of intervening characters. This is because / can't occur in a filename - and those in any path ls might print will only occur singly.

See?

mkdir test; cd test
touch 'long


name' shortname
ln -s l* "$(printf %s.ln l*)"; ln -s s* shortname.ln

LS_COLORS='ln=///\n:lc=:no=//:rc=:rs=:' \
\ls  -l --color=always | sed '\|///|,\|//|!d;//d'

...which prints:

long


name.ln -> long


name
shortname.ln -> shortname

Try it yourself.

9
  • Do you know of any implementation besides modern versions of GNU ls that supports LS_COLORS that way and a --color=always and options after arguments? If on a GNU system, why would you use something that convoluted when you can use find or stat? That also probably won't work if you do some ln -s /// some-file. Commented Dec 12, 2014 at 11:48
  • @StephaneChezales - this indicates at least that it should work similarly with a bsd ls. I remember reading that somewhere else months ago about lss all tending to accept a termcap like syntax. Good point about the ln -s /// thing - but \0NULs work as well - and -Recursively. As to why - well, it's easy to use. In some cases easier than find - and it seemed a little more on topic here than find would have been. In any case, not too many people consider it, so I mention it when I'm reminded.
    – mikeserv
    Commented Dec 12, 2014 at 11:57
  • See the man page or there for FreeBSD. If you want to test things on FreeBSD, you can use live CDs like GhostBSD/mfsbsd in a VM. Commented Dec 12, 2014 at 12:09
  • @StéphaneChazelas - no, i dont need to test it - definitely a wholly different animal. If you care to look, the gnu source is also linked to in the link in this answer. i wonder where i read that other thing? thanks very much for pointing that out though, that was a mistaken assumption im happy to discard. ive edited the answer.
    – mikeserv
    Commented Dec 12, 2014 at 12:29
  • recent versions of tcsh have a ls-F builtin that supports LS_COLORS the same way as some versions of GNU ls, but it resorts to invoking ls -F if you pass any option to it, so that won't work here unless ls is GNU ls on your system. Commented Dec 12, 2014 at 13:09
-1

[On Linux / Bash]

I'd do this:

ls -l | sed -e"s/ \+/ /g" | cut -d' ' -f 9-

The sed command collapses multiple spaces to a single space; the cut extracts the 9th field onwards where the field-separator is a space.

Typical output from this:

libboost_atomic.a
libboost_atomic.so -> libboost_atomic.so.1.57.0
libboost_atomic.so.1.57.0
...
libboost_wserialization.a
libboost_wserialization.so -> libboost_wserialization.so.1.57.0
libboost_wserialization.so.1.57.0

Alternatives are:

ls -l | awk '{ print $9, $10, $11 }'
ls -l | awk '{ $1 = $2 = $3 = $4 = $5 = $6 = $7 = $8 = "" ; print }'

But those are imperfect: the first can output trailing spaces; the second, leading spaces.

2
  • 1
    The question explicitly stated "... without having to pipe through another command ..."
    – Jeff Schaller
    Commented Jul 14, 2015 at 16:25
  • @JeffSchaller: Good point; I agree. I hope, though, that this is still of use to someone wanting an answer to the title question (without the detail in the description).
    – Rhubbarb
    Commented Jul 14, 2015 at 16:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .