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.)
- Note that the OP wants to support both
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, usingeval
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.
- not normalized; that is, components such as
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
Setting Up a Workspace Using Team Explorer Everywhere on Linux
Automatically Kill Process That Consume Too Much Memory or Stall on Linux
Accessing Data Appended to an Elf Binary
Bash And/Or .Bashrc Not Working Properly After Su or Ssh Login Unless Run "Bash" Command
What Is File Hole and How Can It Be Used
How to Trace Per-File Io Operations in Linux
Fast Way to Find Difference Between Two Strings of Equal Length in Perl
Must a Process Group Have a Running Leader Process
How to Use .Notparallel in Makefile Only on Specific Targets
Max Thread Per Process in Linux
Packaging Proprietary Software for Linux
How to List the Files in a Zip Archive Without Decompressing It
How to Use Git Namespace to Hide Branches
Shell Script Function Return a String
Debian 8. Failed to Load Iwlwifi
Command to Zip a Directory Using a Specific Directory as the Root
How to Change Port Number for Jenkins Installation in Ubuntu 12.04