Absolute path to symbolic link - linux

Is there possibility of retrieving absolute path of symbolic file in bash? I cannot use realpath() and readlink() gives path of target. But i need absolute path of that symlink.

Is this an assignment? I can't imagine a real situation where you would not be allowed to use readlink but anyway, just for fun, here is a hack script to get the full path of the symlink provided as argument:
For the example I will use the symlink pidof which points to /bin/ usually, but is a symlink to /sbin/killall5 (Ubuntu)
#!/bin/bash
linkpath="$(which $1)"
line="$(ls -la $linkpath)"
realpath="$(echo $line | awk '{print $NF}')"
echo "Full path is: $realpath"
exit
Output:
user#system:~/Desktop$ ./test.sh pidof
Full path is: /sbin/killall5
user#system:~/Desktop$
The reason this works is because the output of ls shows the full path of symlinks. (At least in Debian distros)

Related

How to dot-dot from non-existent directory in Linux (dir1/non-existent-dir/./file)

Is there any way to dot-dot cd/ls/whatever if the path has an non-existent directory in it? Maybe there is some syntax to do so?
E.g. I need to cat the file valid_dir/file.txt but for some reason I can't to it directly.
$> cat valid_dir/non-existent-dir/../file.txt
In GNU Coreutils, there is realpath to print resolved absolute filenames:
cat "$(realpath -m valid_dir/non-existent-dir/../file.txt)"
The -m (--canonicalize-missing) option is required if there are non-existing components.
If you want relative paths instead, you can define the directory they should be relative to with --relative-to, for example the current directory:
$ realpath --relative-to=. -m valid_dir/non-existent-dir/../file.txt
valid_dir/file.txt

Relative path to the home directory (linux)

When I use pwd it returns:
/home/ahmad/multifit-pretrain-lm/multifit/datasets
I look for a command so that I get the path relative to the home directory
multifit-pretrain-lm/multifit/datasets
I look for a command so that I get the path relative to the home directory
You can use bash parameter substitution here:
echo "${PWD#"$HOME"/}"
$PWD shell variable returns same value as the command pwd
${PWD#"$HOME"/} strips "$HOME"/ at the start from $PWD
Variable substitution is a good solution but since you asked for a command, here is one:
realpath --relative-base="$HOME" .

cpanel shell script on cloud server centos no such file or dir

I have a shell script copy_files.sh which I call once a day using a cron job.
However it has never worked I keep getting no such file or directory.
!#/bin/sh
for f in /home/site1/public_html/admin/data/*.csv
do
cp -v "$f" /home/site2/app/cron_jobs/data/"${f%.csv}".csv
done
I have checked via ssh that all paths are correct I have varified the path to /bin/sh using ls -l /bin/sh I have set perms and user and group to root for copy_files.sh I have disabled php open_basedir protection.
the shell script is in /home/site2/
Any ideas why I am still getting no such file or directory?
Is there anyway to check open_basedir protection is off that said considering the script is owned by root I don't see that being the problem unless it's executed as site2 user and not root?
Because of the way you use shell expansion, the variable in your for loop contains the absolute path to your files. Having the absolute path, means there is no need to use string manipulation (%) nor do you need to add the ".csv" to the filename, just get rid of it all together and provide the directory to which you're copying as your second argument to cp, see the example below.
#!/bin/sh
for f in /home/site1/public_html/admin/data/*.csv; do
cp -v "$f" /home/site2/app/cron_jobs/data
done

Why can't I work with files in ".." from within a symbolic link?

I have the following directory structure:
misha#misha-lmd:~/tmp$ ls -l
total 4.0K
-rw-r--r-- 1 misha lmd 21 Feb 18 21:00 hello.py
lrwxrwxrwx 1 misha lmd 20 Feb 18 21:01 symlink -> /home/misha/nobackup/
Next, I try the following:
misha#misha-lmd:~/tmp$ cd symlink
misha#misha-lmd:~/tmp/symlink$ cat ../hello.py
cat: ../hello.py: No such file or directory
Why doesn't this work?
If I do this instead:
misha#misha-lmd:~/tmp/symlink$ cd ..
misha#misha-lmd:~/tmp$ cat hello.py
print "Hello World!"
Then all is well. cd handles .. properly, but cat doesn't. What is this sorcery, and how do I make things work the way I want them to?
EDIT
OK, thanks to some of the answers here, I've found out a bit more about what's going on. First, cd is not actually an executable, it is a built-in command of the shell (in this case, bash):
misha#misha-lmd:~/tmp$ type cd
cd is a shell builtin
If you man bash, you can find all about the environment variables that bash uses for its housekeeping, including moving around directories. There are other built-ins, like pwd, that have counterparts that are actually executables:
misha#misha-lmd:~/tmp/symlink$ type pwd
pwd is a shell builtin
misha#misha-lmd:~/tmp/symlink$ /bin/pwd
/home/misha/nobackup
misha#misha-lmd:~/tmp/symlink$ /bin/pwd -L
/home/misha/tmp/symlink
The /bin/pwd executable prints the physical path by default, but can also print the logical path given the `-L' switch. Similarly, when I try to do:
misha#misha-lmd:~/tmp/symlink$ cat ../hello.py
cat: ../hello.py: No such file or directory
things are failing because .. is being interpreted as the physical parent directory, not the logical one. This allows me to refine my question as:
When I specify a command-line argument to an executable, how can I get .. to mean the logical parent, not the physical one?
Because the directory .. in your symlinked directory is your home directory.
../something means "go to the .. directory", not "strip the last path component".
You can try pwd -P to see where you are, after you change into symlink.
It doesn't work because there's no /home/misha/hello.py. A symbolic link does not create a new directory, but points to the one linked. So when you cd to a symbolic link, you actually cd to that directory,
Did you expect the shell will remember from where you came from a symbolic link? Well, doesn't work like that :)
Try
cat $(cd ..; pwd)/hello.pycat
Don't know if it helps you.
EDIT:
If you really need it, here is a way to do it (but it's kind of ugly):
$ lcat ( ) ( cd "${1%/*}"; cat "${1##*/}"; )
# use it like 'cat'
$ lcat ../hello.py
Using ( ... ) instead of { ... } for the function body makes it do the cd in a sub shell instead of the current shell.
When I try it it works also with "file name completion".
But this is only solving cat, it can be more complex to use for other commands as well I think.
$ lcmd ( ) ( local cmd="$1"; shift; cd "${1%/*}"; $cmd "${1##*/}"; )
Then do e.g. lcmd cat ../hello.py

How can a bash script know the directory it is installed in when it is sourced with . operator?

What I'd like to do is to include settings from a file into my current interactive bash shell like this:
$ . /path/to/some/dir/.settings
The problem is that the .settings script also needs to use the "." operator to include other files like this:
. .extra_settings
How do I reference the relative path for .extra_settings in the .settings file? These two files are always stored in the same directory, but the path to this directory will be different depending on where these files were installed.
The operator always knows the /path/to/some/dir/ as shown above. How can the .settings file know the directory where it is installed? I would rather not have an install process that records the name of the installed directory.
I believe $(dirname "$BASH_SOURCE") will do what you want, as long as the file you are sourcing is not a symlink.
If the file you are sourcing may be a symlink, you can do something like the following to get the true directory:
PRG="$BASH_SOURCE"
progname=`basename "$BASH_SOURCE"`
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
dir=$(dirname "$PRG")
Here is what might be an elegant solution:
script_path="${BASH_SOURCE[0]}"
script_dir="$(cd "$(dirname "${script_path}")" && pwd)"
This will not, however, work when sourcing links. In that case, one might do
script_path="$(readlink -f "$(readlink "${BASH_SOURCE[0]}")")"
script_dir="$(cd "$(dirname "${script_path}")" && pwd)"
Things to note:
arrays like ${array[x]} are not POSIX compliant - but then, the BASH_SOURCE array is only available in Bash, anyway
on macOS, the native BSD readlink does not support -f, so you might have to install GNU readlink using e.g. brew by brew install coreutils and replace readlink by greadlink
depending on your use case, you might want to use the -e or -m switches instead of -f plus possibly -n; see readlink man page for details
A different take on the problem - if you're using "." in order to set environment variables, another standard way to do this is to have your script echo variable setting commands, e.g.:
# settings.sh
echo export CLASSPATH=${CLASSPATH}:/foo/bar
then eval the output:
eval $(/path/to/settings.sh)
That's how packages like modules work. This way also makes it easy to support shells derived from sh (X=...; export X) and csh (setenv X ...)
We found $(dirname "$(realpath "$0")") to be the most reliable with both sh and bash. As team mates used them interchangeably, we ran into problems with $BASH_SOURCE which is not supported by sh.
Instead, we now rely on dirname, which can also be stacked to get parent, or grandparent folders.
The following example returns the parent dir of the folder that contains the .sh file:
parent_path=$(dirname "$(dirname "$(realpath "$0")")")
echo $parent_path
I tried messing with variants of $(dirname $0) but it fails when the .settings file is included with ".". If I were executing the .settings file instead of including it, this solution would work. Instead, the $(dirname $0) always returns ".", meaning current directory. This fails when doing something like this:
$ cd /
$ . /some/path/.settings
This sort of works. It works in the sense that you can use the $(dirname $0) syntax within the .settings file to determine its home since you are executing this script in a new shell. However, it adds an extra layer of convolution where you need to change lines such as:
export MYDATE=$(date)
to
echo "export MYDATE=\$(date)"
Maybe this is the only way?

Resources