How to Implement Readlink to Find the Path

How to implement readlink to find the path

This Use the readlink() function properly for the correct uses of the readlink function.

If you have your path in a std::string, you could do something like this:

#include <unistd.h>
#include <limits.h>

std::string do_readlink(std::string const& path) {
char buff[PATH_MAX];
ssize_t len = ::readlink(path.c_str(), buff, sizeof(buff)-1);
if (len != -1) {
buff[len] = '\0';
return std::string(buff);
}
/* handle error condition */
}

If you're only after a fixed path:

std::string get_selfpath() {
char buff[PATH_MAX];
ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff)-1);
if (len != -1) {
buff[len] = '\0';
return std::string(buff);
}
/* handle error condition */
}

To use it:

int main()
{
std::string selfpath = get_selfpath();
std::cout << selfpath << std::endl;
return 0;
}

Retrieve path of symbolic link

The readlink() function returns the number of bytes copied to your buffer, without the final \0. This means that if you call readlink() with a buffer of 100 bytes and readlink() returns 100, you need more space (even if the path was exactly 100 bytes, you would still need at least 1 byte to add a null character at the end).

The solution is to increase your buffer in a loop:

size_t bufsize = 255; /* Initial buffer size */
ssize_t result;
char* buf = malloc(bufsize); /* Initial buffer allocation */
while ((result = readlink(path, buf, bufsize)) >= bufsize) {
/* We double the buffer size, so next call should succeed ! */
bufsize *= 2;
buf = realloc(buf, bufsize);
}
buf[result] = '\0';

WARNING: This is just an example, we don't check if readlink returns -1 in case of errors. Same for malloc and realloc. You should check errors in real-world.

How to get full path of a file?

Use readlink:

readlink -f file.txt

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

Use realpath

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

Bash: how to get real path of a symlink?

readlink is not a standard command, but it's common on Linux and BSD, including OS X, and it's the most straightforward answer to your question. BSD and GNU readlink implementations are different, so read the documentation for the one you have.

If readlink is not available, or you need to write a cross-platform script that isn't bound to a specific implementation:

If the symlink is also a directory, then

cd -P "$symlinkdir"

will get you into the dereferenced directory, so

echo "I am in $(cd -P "$symlinkdir" && pwd)"

will echo the fully dereferenced directory. That said, cd -P dereferences the entire path, so if you have more than one symlink in the same path you can have unexpected results.

If the symlink is to a file, not a directory, you may not need to dereference the link. Most commands follow symlinks harmlessly. If you simply want to check if a file is a link, use test -L.

Readlink - How to crop full path?

If you need the last four directory components of the full path, and if you don't have newlines in the full path, and if you have GNU grep or BSD (Mac OS X) grep with support for -o (output only the matched material) then this gives the required result:

$ cek="/home/test/test/2014/10/13/log.file"
$ echo "${cek%/*}"
/home/test/test/2014/10/13
$ echo "${cek%/*}" | grep -o -E -e '(/[^/]+){4}$'
/test/2014/10/13
$ full_path=/home/some/where/hidden/test/2014/08/29/sparefile.log
$ echo "${full_path%/*}" | grep -o -E -e '(/[^/]+){4}$'
/test/2014/08/29
$

I need path starting /201[0-9]:

/home/bla/bla2/bla3/2014/01/13/13…/2014/01/13/13….

So, you need to use grep -o again, starting with the year pattern:

echo "${fullpath%/*}" | grep -o -e '/201[0-9]/.*$'

This is much simpler; you don't even need extended regular expressions for this!

If you need the path element before the year too, then you need:

echo "{fullpath%/*}" | grep -o -e '/[^/][^/]*/201[0-9]/.*$'

How to handle readlink() of /proc/self/exe when executable is replaced during execution?

Instead of using readlink to discover the path to your own executable, you can directly call open on /proc/self/exe. Since the kernel already has an open fd to processes that are currently executing, this will give you an fd regardless of whether the path has been replaced with a new executable or not.

Next, you can use fexecve instead of execv which accepts an fd parameter instead of a filename parameter for the executable.

int fd = open("/proc/self/exe", O_RDONLY);
fexecve(fd, argv, envp);

Above code omits error handling for brevity.



Related Topics



Leave a reply



Submit