Find file's own path - bash

I'm trying to find what the unix equivalent of the Windows/DOS variable %cd% is. What I'm looking for is an environmental variable or a workaround that will let me set a variable to the path of the file currently running.
For example, if the program is in /home/chris/Desktop but the working directory is /home/chris, what would be the command to get ~/Desktop as opposed to pwd which will give me /home/chris.

In BASH, you can look at the $PWDvariable. That'll show your Present Working Directory. Getting the relationship between the $PWD and where the program is located is a bit trickier. You can look at the $0 variable which should give you the name of the file I ran the following script:
#! /bin/bash
#
echo "PWD = $PWD"
echo "\$0 = $0"
And got the following result:
$ test.sh
PWD = /Users/david
$0 = /Users/david/bin/test.sh
The $0 gives you the name of the file from the root of the OS. Taking the dirname will give you the file name. Somehow, if you can filter out the PWD from the $0, you might get what you're looking for. I had some luck with the following:
curPath=$(dirname "${0#$PWD/}")
Didn't thoroughly test it, from what I can see, it seems to do what you want. What it can't do is do something like this:
$ test.sh
PWD = /Users/david/someSubDir
$0 = /Users/david/bin/test.sh
The current path is /Users/david/bin/test.sh
It would have been nice if it could do this:
The current path is ../bin/test.sh
Although the former is correct.
The readlink command doesn't work on non-Linux systems.

How about dirname $(readlink -f $0)
readlink -f $0 returns the canonicalized path to the running script.
dirname removes everything after and including the final \.

This way works, but isn't 100% reliable:
${0%/*}
The way that works is that it reads $0 (the program name), and strips off everything from the final slash onwards. It's not reliable because if your script is invoked via a symlink, you will get the directory containing the symlink, not the directory containing the real script.
Also, it's possible to pass in a "fake" value for $0, for example by using exec -a. So even if you aren't using symlinks, it's still not a 100% solution. In fact, such a solution doesn't exist.

Working with what Chris suggested, you could use the which command. According to the man page, which reports the full path of the executable that would have been executed if its argument had been entered at the shell prompt. Since we know $0 was entered at the shell prompt, we can use `which $0` to report exactly the path that was used to execute. Unfortunately, this still suffers from the symlink issue, as which does not provide options to avoid symlinks.

Related

Changing PWD in a script to allow for accessing file without prefixing full path

I know I should be able to change the current working directory of a bash script by doing something akin to
cd `dirname $MYPATH`
but for some reason this doesn't work (or not as I imagined it).
#!/bin/bash
WAYPATH="/home/user/articles"
TEST_PATH="/home/user/testing"
# Set working directory of the script to be testing
cd `dirname $TEST_PATH`
for i in $(ls $WAYPATH); do
another_command $i $i.r > $TEST_PATH/htmls/$i.html
done
My goal here is to allow the bash script to find the files located in TEST_PATH (which have matching name to those in WAY_PATH) without having to prefix them with the full path (because another_command) makes use of the whole argument passed to it.
So this is a lesson on understanding what commands do after reading about them on Stackexchange. I was using
cd `dirname $MYPATH`
following this answer where they achieved the desired result
cd `dirname $0`
$0 is the full path of the bash script, so dirname is required to return the path without the name of the file.
Instead, for an arbitrary supplied path is sufficient to do a simple
cd $MYPATH
as suggested in comments.

Parent directory of a script

So I am a rookie in Linux and I need some help. I have to write a bash script in which I have to use the parent directory of the script to create a file there, wherever the script would be. It should look like this:
If my script it's in "/home/student/", I need to create, using an in-script command another file called txt in /home/. Any ideas please? Thank you.
There's a subtlety if you want to be able to run your script from anywhere.
eg: if your script is in /home/myHome/someDir/someOther, and you want to create a file in /home/myHome/someDir wherever you are when you run your script.
To solve it, you just need to first derive the directory where your script is.
It can be done using:
SCRIPT_DIRECTORY="$(dirname "$0")"
touch "$SCRIPT_DIRECTORY/../myFile.txt"
Edit: Actually it can be even more subtle, if you want to handle symlinks. ie: if the symlink /home/myHome/mySymlink points at your script, and is the one actually being called, then the previous script will consider /home/myHome/ instead of /home/myHome/someDir/someOther
To handle this case you can do
if [ -L "$0" ] && [ -x $(which readlink) ]; then
ACTUAL_SCRIPT_FILE="$(readlink -mn "$0")"
else
ACTUAL_SCRIPT_FILE="$0"
fi
SCRIPT_DIRECTORY="$(dirname "$ACTUAL_SCRIPT_FILE")"
touch "$SCRIPT_DIRECTORY/../myFile.txt"
use .. to point to parent directory. So you could create a file using something like
MY_SCRIPTDIR="$(dirname $0)"
touch ${MY_SCRIPTDIR}/../abc.txt
From your command prompt or within shell script.
Unfortunately, the other answers either give you the current working directory instead of the directory the script is in, or they will not work if either the script or one of the directories along the way is a symbolic link rather than a real directory.
What will work is:
dirname $(readlink -f "$0")
Explanation:
"$0" is the name of the script as you type it in your command line. Quoting is important for the case it contains whitespace.
readlink will resolve any symbolic links along the way
dirname takes just the directory name from script's full path - it's better readable and safer for corner cases than manually looking for slashes etc.
Now, you will get the correct result even in a complex case: if your script is in /tmp and you create a symbolic link to it in /tmp/abc/, and your current directory will be /home and you run /tmp/abc/your-script, it will correctly output /tmp, not /home nor /tmp/abc.

Bash script awkwardness with pwd

I've got a strange issue while working with a bash script. Here it is:
PWD=${pwd}
# several commands
cd /etc/nginx/sites-enabled/
# more commands
cd $PWD
# I expect that I returning to my directory,
# but $PWD contains current dir - /etc/nginx/sites-enabled/
This behavior is kind of lazy. $PWD stores command, which calculates the current directory and returns it at the moment we call $PWD, but I want to store the string variable in it. How to do that?
PWD is an environmental variable and is changed when you change the directory.
Use a different name for the variable,
eg:
MYPWD=${PWD} #or MYPWD=$(pwd)
cd /etc/nginx/sites-enabled/
cd $MYPWD
Try:
PWD=`pwd`
Or:
PWD=$(pwd)
Both expressions will execute the pwd command and store the command output in the shell variable PWD. There is plenty of discussion on the web about when to use each style. The one point that I recall is that the "$(cmd)" approach allows for nesting of commands, e.g.
CURRENT_BASENAME=$(basename $(pwd))
Edit - It just occurred to me that PWD is a built in shell variable that always expands to the current working directory.
you may also find
cd -
usefull

$PWD vs. pwd regarding portability

I'm writing a shell script which parses the path of the current working directory (printing a like of all basenames above the current directory).
So far, I've been using the environment variable PWD to parse the path but I wonder if
I can count on PWD to be always set
to give the same result on every platform
Would it possibly be better to use the pwd shell-builtin? I need this script to run on as many platforms as possible, so I just wondered...
POSIX requires $PWD to be set in the following fashion:
PWD
This variable shall represent an absolute pathname of the current working directory. It shall not contain any components that are dot or dot-dot. The value is set by the cd utility, and by the sh utility during initialization.
So you can rely on that being set – but do note "... an absolute path...", not the absolute path.
bash (at least recent versions) will remember what symlinks you followed when setting $PWD (and the pwd builtin). command pwd (that is, the external command) will not. So you'll get different results there, which might, or might not, be important for you. Use pwd -P if you want a path without symlinks.
Do note that the pwd documentation states:
If an application sets or unsets the value of PWD, the behavior of pwd is unspecified.
So, don't do that :)
In short, there is no winner here. The environment variable will be there in POSIX shells, as will the external command and possibly a built-in too. Choose the one that best fits your need, the important thing being whether you care about symlinks or not.
From that forum article, "$PWD vs `pwd`" which compares AIX 4.2.1, AIX 6, Sparc Solaris 10 and Redhat 5 enterprise with this regard:
there is no difference between $PWD and builtin pwd,
there is no difference between builtin pwd -P and /usr/bin/pwd.
The former shows working directory with names of symbolic links whereas the latter displays actual path.
The only discrepancy is that external command is in /usr/bin in most systems and /bin in Redhat.
Another point to note is
command substitutions are not generally safe on trailing
newlines .
This is obviously fairly contrived, but if you're really concerned about safely
handling input you should be using "$PWD". See, for example:
$ my_dir=$'/tmp/trailing_newline\n'
$ mkdir -p "$my_dir"
$ cd "$my_dir"
$ pwd
/tmp/trailing_newline
$ printf "%q\n" "$(pwd)" "$PWD"
/tmp/trailing_newline
$'/tmp/trailing_newline\n'
$ cd "$(pwd)"
sh: cd: /tmp/trailing_newline: No such file or directory
$ cd "$PWD"
It is possible to work around the command substitution but it is by no means
pretty. You can append a trailing character and then strip it with a
parameter expansion:
$ pwd_guarded="$(pwd; printf '#')"
$ pwd_fixed="${pwd_guarded%$'\n'#}"
$ printf "%q\n" "$pwd_fixed"
$'/tmp/trailing_newline\n'
$ cd "$pwd_fixed"
This is particularly ugly because you then also have to strip the newline that
pwd adds, which would normally have been stripped by the command substitution.
This becomes a total mess if you don't resort to non-POSIX constructs like
$'', so basically, just use "$PWD" if you care about these things. Of course
it is perfectly reasonable to just not support trailing newlines in directory
names.
If you know that bash is available and the script is executed with it, PWD is safe.
If, on some systems, only plain sh is available, use pwd.
If it were me, I'd use pwd since it is a built-in both for bash and sh. That does not mean they work identically in all respects, but if you are invoking it without options, that shouldn't matter.

In bash2, how do I find the name of a script being sourced?

Here's my situation:
I have multiple versions of a script in source code control where the name differs by a path name component (ex: scc/project/1.0/script and scc/project/1.1/script). In another directory, there is a symlink to one of these scripts. The symlink name is not related to the script name, and in some cases may change periodically. That symlink, in turn, is sourced by bash using the '.' command.
What I need to know: how do I determine the directory of the referenced script, on a 10 year-old system with Bash 2 and Perl 5.5? For various reasons, the system must be used, and it cannot be upgraded.
In Bash 3 or above, I use this:
dir=`perl -MCwd=realpath -MFile::Basename 'print dirname(realpath($ARGV[0]))' ${BASH_SOURCE[0]} $0`
Apologies for the Perl one-liner - this was originally a pure-Perl project with a very small amount of shell script glue.
I've been able to work around the fact that the ancient Perl I am using doesn't export "realpath" from Cwd, but unfortunately, Bash 2.03.01 doesn't provide BASH_SOURCE, or anything like it that I've been able to find. As a workaround, I'm providing the path information in a text file that I change manually when I switch branches, but I'd really like to make this figure out which branch I'm using on its own.
Update:
I apologize - apparently, the question as asked is not clear. I don't know in every case what the name of the symlink will be - that's what I'm trying to find out at run time. The script is occasionally executed via the symlink directly, but most often the link is the argument to a "." command running in another script.
Also, $0 is not set appropriately when the script is sourced via ".", which is the entire problem I'm trying to solve. I apologize for bluntness, but no solution that depends entirely upon $0 being set is correct. In the Perl one-liner, I use both BASH_SOURCE and $0 (BASH_SOURCE is only set when the script is sourced via ".", so the one-liner only uses $0 when it's not sourced).
Try using $0 instead of ${BASH_SOURCE[0]}. (No promises; I don't have a bash 2 around.)
$0 has the name of the program/script you are executing.
Is stat ok? something like
stat -c %N $file
bash's cd and pwd builtins have a -P option to resolve symlinks, so:
dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
works with bash 2.03
I managed to get information about the porcess sourcing my script using this command:
ps -ef | grep $$
This is not perfect but tells your which is the to process invoking your script. It migth be possible with some formating to determine the exact source.

Resources