Determine If Relative or Absolute Path in Shell Program

Check If Shell Script $1 Is Absolute Or Relative Path

[ ... ] doesn't do pattern matching. /* is being expanded to the contents of /, so effectively you have

if [ "$DIR" = /bin /boot /dev /etc /home /lib /media ... /usr /var ]

or something similar. Use [[ ... ]] instead.

if [[ "$DIR" = /* ]]; then

For POSIX compliance, or if you just don't have a [[ that does pattern matching, use a case statement.

case $DIR in
/*) echo "absolute path" ;;
*) echo "something else" ;;
esac

Determine if relative or absolute path in shell program


if [[ "$0" = /* ]]
then
: # Absolute path
else
: # Relative path
fi

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

Bash: check if a relative path exists


However, if the working directory is /Users/keith/Documents/ and I call my script and pass just "TestFolder", then the test in this if statement will evaluate to false, even though TestFolder exists within the current working directory.

That's not correct. If TestFolder exists in the current working directory, [ -d TestFolder ] will succeed. So there is no problem here.

By the way, if you want to canonicalize a filepath and you have Gnu coreutils (which you probably do if you have a Linux system), then you can use:

readlink -f "$path"

or

readlink -e "$path"

The difference is that -f will succeed even if the last component in the path doesn't exist (so it will succeed if the path is a plausible name for a file to be created), while -e will only succeed if the path resolves to an existing file or directory. See man readlink for more details.

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.

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 -.

Shell scripting: extracting absolute path from default value of path variable


I want to understand the reason why ~/Downloads as the default value of src_dir does not work.

From bash manual tilde expansion:

If a word begins with an unquoted tilde character (‘~’), ...

The "${2:-~/Downloads}" doesn't begin with an unquoted tilde character. And the result ~/Downloads is quoted within double ". The ~ is not expanded. So readlink -f -- "~/Downloads" is run. Because the path named literally /home/ashwin/~/Downloads doesn't exists on your system, readlink generates no output and exits with a nonzero exit status (1 on my system).

Please do not use backticks `. Use $( ) instead.



Related Topics



Leave a reply



Submit