Expand a Possible Relative Path in Bash

How to retrieve absolute path given relative

use:

find "$(pwd)"/ -type f

to get all files or

echo "$(pwd)/$line"

to display full path (if relative path matters to)

How to obtain the absolute path of a file via Shell (BASH/ZSH/SH)?

Use realpath

$ realpath example.txt
/home/username/example.txt

Reliable way for a Bash script to get the full path to itself

Here's what I've come up with (edit: plus some tweaks provided by sfstewman, levigroker, Kyle Strand, and Rob Kennedy), that seems to mostly fit my "better" criteria:

SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"

That SCRIPTPATH line seems particularly roundabout, but we need it rather than SCRIPTPATH=`pwd` in order to properly handle spaces and symlinks.

The inclusion of output redirection (>/dev/null 2>&1) handles the rare(?) case where cd might produce output that would interfere with the surrounding $( ... ) capture. (Such as cd being overridden to also ls a directory after switching to it.)

Note also that esoteric situations, such as executing a script that isn't coming from a file in an accessible file system at all (which is perfectly possible), is not catered to there (or in any of the other answers I've seen).

The -- after cd and before "$0" are in case the directory starts with a -.

finding an absolute path from a supplied path in bash

The ${varname:-default} notation means "substitute the value of the variable named varname, if it's set and non-empty; otherwise, substitute the string default".

In your case, "$( cd "$(dirname ${OUTP})" && pwd)" is not the name of a variable, so ${"$( cd "$(dirname ${OUTP})" && pwd)":-"/home/default/output/dir"} is not using the above notation; it's just gibberish.

Also, the dirname call doesn't make sense to me; I think you might be misunderstanding what that utility does.

Overall, I think what you want is:

OUTPUT_PATH="$(cd "${OUTP:-/home/default/output/dir}" && pwd)"

You'll also want some error-checking afterward, to ensure that $OUTPUT_PATH is actually set (i.e., that cd was able to move to the specified directory).

Expanding a relative path to a full path when running a C program in shell

Your question is not very well worded so I am not sure I am answering the same question that you are asking, but you can convert "./myScript" to its full path using the realpath() function.

How do you normalize a file path in Bash?

if you're wanting to chomp part of a filename from the path, "dirname" and "basename" are your friends, and "realpath" is handy too.

dirname /foo/bar/baz 
# /foo/bar
basename /foo/bar/baz
# baz
dirname $( dirname /foo/bar/baz )
# /foo
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp

realpath alternatives

If realpath is not supported by your shell, you can try

readlink -f /path/here/.. 

Also

readlink -m /path/there/../../ 

Works the same as

realpath -s /path/here/../../

in that the path doesn't need to exist to be normalized.

Find absolute path in shell script run by superuser for a path given by regular user. Resolve paths stored in variables to absolute paths portably

Note: The OP's problem is ultimately unrelated to the fact that the script is run with sudo (as superuser), because ~ and $HOME by default still reflect the real user's home dir when running with sudo - unless you use sudo -H.

  • The primary problem is that the input path (stored in $REL_PATH) starts with a literal ~ (due to being part of a double-quoted string), whereas the shell only applies tilde expansion to unquoted ~ instances at the beginning of the word.

  • However, the ~ must be quoted in $REL_PATH so as to defer expansion until the path should be determined in the context of the user running the script.

    • Note that the OP wants to support both ~ (current user's home dir.) and ~<username> expansions (<username>'s home dir.)

It is therefore tempting to simply pass $REL_PATH to eval as a whole, but that is not only risky1, but is likely to break with paths containing shell metacharacters such as & and have leading or trailing whitespace or contain run of two or more spaces.

Therefore, the following, POSIX-compliant solution:

  • isolates the ~ token at the beginning of a path and expands just it, using eval safely,
  • then puts the path back together by appending the rest of the path to the expanded token.
#!/bin/sh

# Note: Assumes that:
# * $REL_PATH is set to the input path.
# * $user is set to the real username.

case $REL_PATH in
\~|\~/*) # a ~ or ~/... path, expand the ~ based on the real user.
# NOTE: If you know that `sudo` is never invoked with `-H`,
# you could simply use `$HOME` instead of `$(eval "echo ~$user")`.
ABS_PATH="$(eval "echo ~$user")${REL_PATH#\~}"
;;
\~*) # a ~<username> or ~<username>/... path; expand the ~<username> part.
otherUserHomeDirRef=${REL_PATH%%/*}
# Make sure that $otherUserHomeDirRef is a well-formed
# ~/<username> token, so that `eval` can be safely applied.
if ! expr "$otherUserHomeDirRef" : '~[a-zA-Z0-9][a-zA-Z0-9._-]*$' >/dev/null; then
echo "ERROR: Malformed input path: $REL_PATH" 1>&2
exit 2
fi
otherUserHomeDir=$(eval echo "$otherUserHomeDirRef")
if [ "$otherUserHomeDirRef" = "$REL_PATH" ]; then
ABS_PATH=$otherUserHomeDir
else
ABS_PATH="$otherUserHomeDir/${REL_PATH#*/}"
fi
;;
/*) # already an absolute path, use as-is
ABS_PATH=$REL_PATH
;;
*) # a relative path, resolve based on *current* dir
ABS_PATH="$PWD/$REL_PATH"
esac

# Print the absolute path.
printf '%s\n' "$ABS_PATH"
  • Only POSIX-compliant parameter expansions are used.
  • POSIX-utility expr is used for regex matching.
  • Use of eval is safe here, because the input is either known to be safe or explicitly checked beforehand.
  • The resulting absolute path is, per the OP's requirements:

    • not normalized; that is, components such as . and .. are retained.
    • not checked for existence.

Note that it's generally not a good idea to use all-uppercase variable names such as ABS_PATH, because they can conflict with special shell and environment variables.


[1] Of course, the greater risk in the OP's script is the sourcing of the config file, which could contain any shell code.



Related Topics



Leave a reply



Submit