Search code examples
bashzsh

test -L command returns different error code when called from prompt than from inside the script


I have one folder and one link to the folder:

lrwxr-xr-x    1 xxx staff    9 Dec 12 08:59 _myOrg -> ../_myOrg
drwxr-xr-x   33 xxx staff 1.1K Apr 12 16:47 xxx

when I run test -L _myOrg and then echo $? and then test -L xxx and then echo $? I get – as expected – 0 and 1 respectively.

However, when I run it from the script below:

#!/bin/zsh

pushd $tyc > /dev/null

for d in */ ; do
    if test -L d 
    then
        echo 0
    else
        echo 1
    fi
done

popd > /dev/null

I'm getting:

1
1

and not expected

0
1

Could someone point me in the right direction?


Solution

  • Suppose you have:

    % tree
    .
    ├── xxx
    ├── yyy -> /tmp/tgt
    └── zzz
    

    And:

    % ls -l
    total 0
    drwxr-xr-x  2 dawg  staff  64 Apr 25 16:38 xxx
    lrwxr-xr-x  1 dawg  staff   8 Apr 25 16:45 yyy -> /tmp/tgt
    drwxr-xr-x  2 dawg  staff  64 Apr 25 17:24 zzz
    

    You are using the following glob:

    for d in */ ; do
        [[ -L "$d" ]] && echo "$d is a link" || echo "$d is not a link"
    done
    

    Which results in:

    xxx/ is not a link
    yyy/ is not a link
    zzz/ is not a link
    

    Notice the trailing /. Now change the glob:

    for d in * ; do
        [[ -L "$d" ]] && echo "$d is a link" || echo "$d is not a link"
    done
    

    And it works:

    xxx is not a link
    yyy is a link
    zzz is not a link
    

    Or use your glob and remove the final / in the test:

    for d in */ ; do
        [[ -L "${d: : -1}" ]] && echo "$d is a link" || echo "$d is not a link"
    done
    

    Or, test to see if there is a trailing / and remove it if so. This now works with both styles of globs:

    for d in */ ; do
        td="${d%/}"
        [[ -L "$td" ]] && echo "$d is a link" || echo "$d is not a link"
    done
    

    Either of those prints:

    xxx/ is not a link
    yyy/ is a link
    zzz/ is not a link
    

    Note:

    • You also need the sigil -- the $ part -- to reference the value in the looped variable. The way you have written it, test is attempting to test a literal d and silently failing if the file is not found.
    • You should generally use "double quotes" around variables in the shell unless you have a good reason not to do so (like around a glob) or the result is subject to further processing by the shell resulting in something different than you are expecting.
    • A path with the trailing / refers to that paths CONTENTS - not the path itself. This is why yyy/ is not a link but yyy is a link. See filepath misconceptions.