bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Is there a special variable for the directory where the script is in


From: Greg Wooledge
Subject: Re: Is there a special variable for the directory where the script is in?
Date: Fri, 12 Feb 2010 09:39:42 -0500
User-agent: Mutt/1.4.2.3i

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>




reply via email to

[Prev in Thread] Current Thread [Next in Thread]