3

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' ]
    then

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

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

    fi

done

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.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
iRomul
  • 43
  • 1
  • 1
  • 5
  • 2
    Quote everything. – ctrl-alt-delor 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 09: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

2 Answers2

4

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

#!/usr/bin/perl
#
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 } }' * .*
roaima
  • 107,089
  • 14
  • 139
  • 261
0

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"
OLDIFS=$IFS
IFS=$'\n'

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

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

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

done    

IFS=$OLDIFS
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.

iRomul
  • 43
  • 1
  • 1
  • 5