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
Partial Specialization of Function Templates
Calling an Explicit Constructor with a Braced-Init List: Ambiguous or Not
Clion C++ Can't Read/Open .Txt File in Project Directory
What Does It Mean to Return a Reference
Duplicate Const Qualifier Allowed in C But Not in C++
Extract C++ Template Parameters
Calling Delete on Null Pointers - C++03 VS C++11
How to Benchmark Boost Spirit Parser
C++. Error: Void Is Not a Pointer-To-Object Type
Why There Is No Std::Copy_If Algorithm
Stl Ordering - Strict Weak Ordering
Qmetaobject::Connectslotsbyname: No Matching Signal
Class' Keyword in Variable Definition in C++
How to Iterate Over Two Vectors Simultaneously Using Boost_Foreach