I need to make a list of all direct symbolic links in a directory, i.e. symbolic links that point to another file which is not a symbolic link.
I tried to do it this way:

for i in $(ls -1A); do

    first_type=$(ls -l $i | cut -b 1)

    if [ $first_type == 'l' ]

        next=$(ls -l $i | awk '{print $NF}')
        next_type=$(ls -l $next | cut -b 1)

        if [ $next_type != 'l' ]
            #Some code



But in this case, the script skips files whose names have spaces/tabs/newlines (including at the start and end of the file name). Is there any way to solve this?

I working on Solaris 10. There is no readlink or stat command. The find command does not have -printf.

  • 2
    Quote everything. Apr 22 '15 at 20:30
  • It looks next=$(ls -l $i | grep '.*-> ' | sed 's/.*-> //') gives desired result, but it looks not optimal
    – iRomul
    Apr 22 '15 at 23:50
  • @iRomul you can dispense with the grep and use sed -n 's/.*-> //p'. Unfortunately it still fails if the filename contains -> or a newline.
    – roaima
    Apr 23 '15 at 9:35
  • 1
    Variations on ln -s /etc/hosts sym ; perl -e 'print readlink,"\n" foreach @ARGV' sym will give you approximations to readlink
    – roaima
    Apr 24 '15 at 10:44

I can provide you with a perl snippet to do this for you:

foreach my $i (@ARGV) {
    # If it is a symlink then...
    -l $i and do {
        # First indirection; ensure that it exists and is not a link
        my $j = readlink($i);
        print "$i\n" if -e $j and ! -l $j

If you save that as /usr/local/bin/if-link and make it executable (chmod a+x /usr/local/bin/if-link) you can use it like this

/usr/local/bin/if-link * .*

To incorporate it in another script, you can use it as a one-liner

perl -e 'foreach my $i (@ARGV) { -l $i && do { my $j = readlink($i); print "$i\n" if -e $j and ! -l $j } }' * .*

In addition to solutions using Perl I decided to try to implement my own mini version readlink using readlink syscall. The source code of the utility using is very simple and looks like this:

ret_length = readlink( argv[1], buffer, buffer_size );

if( ret_length >= 0 ) {

    buffer[ ret_length ] = '\0';
    printf( "%s\n", buffer );
    return 0;

} else {    
    return errno;   

bash script (./xrl is name of utility; filenames that contains newline is unsupported):

USAGE="Usage: chl_xrl filename"

if [[ $# != 1 ]]
    echo $USAGE
    exit 1

ls -1Au | while read -r pos; do

    if [[ $(./xrl "$pos") == $1 ]]
        echo "$pos"


exit 0

The one refinement - script takes a filename as parameter and looking for a direct link, but not for all files in a directory.

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.