On Fri, Feb 12, 2010 at 02:53:39PM +0100, Bernd Eggink wrote: > I once wrote a more generic shell function for this purpose, see: > http://sudrala.de/en_d/shell-getlink.html
You note that it doesn't handle names containing ->, which is true. I'll get back to that at the end. It also won't handle any name that "ls -l" will refuse to print out correctly on any given system. (I don't know any specific cases off hand, though. When stdout isn't a terminal, both Debian and OpenBSD seem to stop converting weird bytes into question marks. And HP-UX gladly prints weird bytes even when stdout is a terminal. But there could be some strange legacy Unix systems out there where ls doesn't print things as expected.) Also, there are three more cases that it can't handle. The first is due to missing quotes in your command: echo ${link##*-> } Without quotes, this will mangle all leading, trailing or repeated whitespace. Easily fixed by adding the quotes. (There are a few other cases of missing quotes too.) The second is a target named -n or -e. "echo -n" or "echo -e" will print nothing. Again, this is easily fixed by replacing echo with printf "%s\n" (assuming you want to continue printing the newline). The third case is harder. Trailing newlines will be eaten up by the command substitution here: link=$(command ls -l "$file") This is somewhat complicated by the fact that "ls -l" adds a newline, which you do want to remove; but you don't want to remove a newline that's actually part of the filename. The only trick I know for getting the output of a command into a variable, *including trailing newlines*, is this: variable=$(some command; printf x); variable=${variable%x} Or in your case, since you want to remove precisely one newline (even if there are more than one): link=$(command ls -l -- "$file"; printf x) link=${link%$'\nx'} That leaves names which contain ->. The tricky part here is that we can't easily tell whether an extra -> is in the symbolic link or in the target. imadev:~$ ln -s tmp 'x -> y' imadev:~$ ln -s 'y -> tmp' x imadev:~$ ls -ld x* lrwxr-xr-x 1 wooledg pgmr 8 Feb 12 09:28 x -> y -> tmp lrwxr-xr-x 1 wooledg pgmr 3 Feb 12 09:28 x -> y -> tmp However, there actually is enough information available to extract the desired part. When we call ls -l, we're passing it the filename we're resolving. So we already know the source name. Removing the source name and the ' -> ' which follows it should leave us with the target name. link=$(command ls -l -- "$file"; printf x) link=${link%$'\nx'} remove="$file -> " file=${link#*$remove} And testing: imadev:~$ file=$HOME/x imadev:~$ link=$(command ls -l -- "$file"; printf x) imadev:~$ link=${link%$'\nx'} imadev:~$ remove="$file -> " imadev:~$ file=${link#*$remove} imadev:~$ printf "<%s>\n" "$file" <y -> tmp> imadev:~$ file=$HOME/'x -> y' imadev:~$ link=$(command ls -l -- "$file"; printf x) imadev:~$ link=${link%$'\nx'} imadev:~$ remove="$file -> " imadev:~$ file=${link#*$remove} imadev:~$ printf "<%s>\n" "$file" <tmp>