10

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
7

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
  • stat -f "%N -> %Y" -- /usr/local/bin/mvn worked great. Thanks! – Chris Snow Dec 12 '14 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 Aug 31 '16 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. – Stéphane Chazelas Aug 31 '16 at 9:26
5

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.

  • cool, learn something new every day. – rob Dec 12 '14 at 9:59
  • @Sree - Many thanks! I had tried the ls options, but not file. Unfortunately, neither seems to work :( – Chris Snow Dec 12 '14 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. – Sree Dec 12 '14 at 10:08
  • @Sree - no worries, and sorry for the confusion. I've upvoted your answer anyway as it will be useful when I'm back on a linux machine. – Chris Snow Dec 12 '14 at 10:10
  • maybe check 'readlink' utility on osx. – tonioc Dec 12 '14 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.

  • 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. – Stéphane Chazelas Dec 12 '14 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 Dec 12 '14 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. – Stéphane Chazelas Dec 12 '14 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 Dec 12 '14 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. – Stéphane Chazelas Dec 12 '14 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.

  • The question explicitly stated "... without having to pipe through another command ..." – Jeff Schaller Jul 14 '15 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 Jul 14 '15 at 16:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.