C program, that prints its executable file name - c++

Suppose source code file name is test.cpp. When it is compiled, it produce test.exe file. When I execute it, it should identify its file name that is test.exe and print it.
I'm able to get list of all the files and directories present in the current directory using following code:
DIR * directory;
struct dirent * direct;
direct = readdir(directory);
But, how can I identify the associated file name, in this case which is "test.exe"?

In your main function, argv[0] is the name of the executable from the command line
#include <stdio.h>
int main(int argc, char ** argv)
{
printf("%s", argv[0]);
return 0;
}
Live Demo
This prints the command name, which is the directory relative to the current working directory, plus the executable name (if available, which is not guaranteed)
To get the current working directory, use getcwd() standard library C function.
Extracting the file name from the command path in argv[0] is platform specific : unix use slashes '/', windows allows mixed uses of slash / and backslash \, and any other platform could use any other path separator. Extracting the file name from a path requires a cross-platform library, such as Qt or Boost. On POSIX environments, basename could be used.

#include <stdio.h>
int main(int argc, char *argv[])
{
printf("%s\n", argv[0]);
return 0;
}
Note that your program can be launched as:
/home/user/./app
In this case you can get the name by using strrchr:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *appname;
appname = strrchr(argv[0], '/'); /* '\\' on Windows */
printf("%s\n", appname ? ++appname : argv[0]);
return 0;
}

You know the name of the executable when you're building it; the
simplest solution is to embed it in the program, using a -D or /D
option to define a macro on the command line.
Other than that, the generic answer is that it isn't possible:
According to the standard
argv[0] should contain the name which was used to invoke the
program (whatever that means). Which is nice, but 1) it isn't even
implementable under Unix, and 2) under most systems, there are all sorts
of aliasing which means that the name used to invoke the program bears
no relationship to the name of the executable.
Under Windows
There's a system function GetModuleFileName which can be used to
obtain the path of the executable. Once you've got the path, the last
element of the path is the name of your executable.
Under Unix
It's fundamentally impossible. When starting a new process, Unix
takes separate arguments for the path to the executable and for what
ends up in argv[0], so they can potentially have no relationship to
one another. It all depends on who starts your process. bash will
put the full path to the executable in the environment variable "_",
so you can use getenv to get it. But that only works if your program
was started by bash. On most Unices, you can also find it in the
/proc filesystem, if you know your way around there; but the
organization of this varies from one Unix to the next. Note too that
because of hard links, you're executable may not have just one
name.
The real question is why you want to do this. What is the problem you
are trying to solve?

On Linux specifically:
you might use proc(5) and the /proc/self/exe symlink, so use readlink(2) on that. Once you did that, you could use basename(3) or realpath(3) on the obtained symlink.
However, be aware that a program might not always have a file path. It could have several file paths (for example /bin/rbash is a symlink to /bin/bash, and the shell process behave differently when invoked as rbash or as bash). Sometimes a given file (actually an inode, see inode(7)) has several hardlinks to it. In weird cases there is none.
And it could happen that a program is execve(2)-ed and then removed with unlink(2) (perhaps from another process scheduled to run before yours) etc..
(BTW, your question is OS specific; readdir(3) is POSIX and there are some operating systems not even having directories, and readdir is not mentioned by the C11 standard; check by reading n1570)
I experimented with the following (pathological) ./selfremove program (in my /home/basile/tmp/ directory) which removes its own binary:
// file selfremove.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main (int argc, char **argv)
{
char selfpath[128];
memset (selfpath, 0, sizeof (selfpath));
if (readlink ("/proc/self/exe", selfpath, sizeof (selfpath) - 1) < 0)
{
perror ("first readlink");
exit (EXIT_FAILURE);
};
printf ("initial /proc/self/exe -> %s\n", selfpath);
if (unlink (argv[0]))
{
fprintf (stderr, "unlink %s: %m\n", argv[0]);
exit (EXIT_FAILURE);
};
printf ("%s unlinked\n", argv[0]);
if (readlink ("/proc/self/exe", selfpath, sizeof (selfpath) - 1) < 0)
{
perror ("second readlink");
exit (EXIT_FAILURE);
};
printf ("final /proc/self/exe -> %s\n", selfpath);
return 0;
}
It works, and the kernel is constructing a * (deleted) symlink (since a perverse coder might rename an executable as selfremove (deleted), so that kernel added suffix is only an indication ....):
% ./selfremove
initial /proc/self/exe -> /home/basile/tmp/selfremove
./selfremove unlinked
final /proc/self/exe -> /home/basile/tmp/selfremove (deleted)
So even with /proc/self/exe you cannot always trust the result.
If you assume that your program has been execve(2)-ed by some shell (or similar program doing execvp(3)) -and that is not always the case- then the PATH variable might have been used (and searched from your main's argv[0] if it has no /). You might use getenv(3) as getenv("PATH") to get it from your environment (see environ(7) for more). That $PATH is generally set and used, but they are pathological cases too.
So in general there is no way to reliably print its own executable (as my pathological selfremove.c demonstrates). In most cases you could find it (e.g. by readlink of /proc/self/exe, or by searching in $PATH using argv[0]).

In Linux systems, I think you can also use basename(char *path); from libgen.h,
try $ man basename:
Print NAME with any leading directory components removed. If specified, also remove a trailing SUFFIX.
I tried it as:
// filename.c
#include<stdio.h>
#include<stdlib.h>
#include<libgen.h>
int main(int argc, char* argv[]){
char* exe_name = basename(argv[0]);
printf(" Executable Name: %s", exe_name);
printf("\n");
return EXIT_SUCCESS;
}
Observations,compiled and tested it as:
taxspanner#:~$ ls filename.c
filename.c
taxspanner#:~$ gcc -std=gnu99 -Wall -pedantic filename.c -o filename
taxspanner#:~$ ls filename*
filename filename.c
taxspanner#:~$
Now run it in current directory:
taxspanner#:~$ ./filename
Executable Name: filename
Using absolute path:
taxspanner#:~$ pwd
/home/taxspanner
taxspanner#:~$ /home/taxspanner/filename
Executable Name: filename
Relative path:
taxspanner#:~$ cd study/divide-5/
taxspanner#:~/study/divide-5$ pwd
/home/taxspanner/study/divide-5
taxspanner#:~/study/divide-5$ ls ../../filename*
../../filename ../../filename.c
taxspanner#:~/study/divide-5$ ../../filename
Executable Name: filename
taxspanner#:~/study/divide-5$
one more try with executable name with suffix:
taxspanner#:~$ gcc -std=gnu99 -Wall -pedantic filename.c -o filename.out
taxspanner#:~$ ./filename.out
Executable Name: filename.out
taxspanner#:~$ cd study/divide-5/
taxspanner#:~/study/divide-5$ ../../filename.out
Executable Name: filename.out
Be careful when using it: Both dirname() and basename() may modify the contents of path, so it may be desirable to pass a copy when calling one of these functions.
Give it a try!!

Related

Getting Environment Variable $PATH in Linux using C++ in Eclipse

I am trying to get the value of environment variable $PATH in Linux using a simple C++program as bellow:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main ()
{
// get PATH using pipe
FILE* pip = popen("exec bash -c 'echo $PATH'", "r");
if (!pip)
{
printf("can not open pipe!");
return 1;
}
char lineversion[600];
memset (lineversion, 0, sizeof(lineversion));
if (!fgets(lineversion, sizeof(lineversion), pip))
{
printf("fgets error!");
return 1;
}
std::cout << lineversion << std::endl;
// get PATH using getenv
char* pPath = getenv ("PATH");
std::cout << pPath << std::endl;
}
I used two different methods: using pipe and using getenv method. They both output this:
/opt/texbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/netbeans-7.3.1/bin
Interestingly the actual value of PATH is something different!
Why my C++ program is showing a different value for PATH?
Edit 1: I am running my C++ program in Eclipse IDE.
Edit 2: Compiling the program directly (without Eclipse IDE) shows the correct PATH value!
Edit 3: I found the answer in here too.
A process inherits the environment from the process that created it.
This is how Linux works, along with many other operating systems.
If you launch a program from Eclipse, that program inherits Eclipse's environment.
If you launch a program from the shell, that program inherits the shell's environment, including the modifications to PATH you have in your init files.
Since Eclipse has inherited its environment from whichever process launched it, you should see the expected output if you launch Eclipse from the shell instead of through your desktop GUI.

Why does the behavior of mkdir differs on different Linux distros?

I was trying to create folders named 1 2 3 4, using the C++ program below. I can successfully achieve that in RHEL.
However it created a folder named {1..4} in ubuntu 13.10.
Why does this happen? Thank you for helping me!
#include <cstdlib>
int main()
{
std::system("mkdir {1..4}");
}
It's a part of CPP unit test in our product. Yes, it's ugly. But I am afraid very few thing can be done in this situation.
You are right.
In RHEL,
sh -c 'echo {1..4}'
1 2 3 4
In Ubuntu
sh -c 'echo {1..4}'
{1..4}
So I use the program below instead. It works!
#include
int main()
{
std::system("bash -c 'mkdir {1..4}'");
}
seems system use sh by default....Thank you for your answer!
A bit of terminology: Linux has directories in its file systems, not "folders" (folders may appear visually on the desktop, but that is a desktop detail).
You don't need to use system(3) (which is running sh not bash!).
And POSIX sh don't know the {1..4} notation, hence the {1..4} string is passed verbatim to /bin/mkdir command (see mkdir(1) ...).
Run
sh -c 'echo {1..4}'
to test that sh don't understand the {1..4} notation.
(so it is a bug in your old RHEL, where perhaps /bin/sh is a symlink to /bin/bash while on Debian and Ubuntu it is a symlink to the more Posix compliant and faster /bin/dash)
Just use the mkdir(2) syscall and code
#include <cstdlib>
#include <cstdio>
#include <sys/stat.h>
#include <sys/types.h>
int main() {
for (int i=1; i<=4; i++) {
char buf[8];
snprintf(buf, sizeof(buf), "%d", i);
if (mkdir(buf, 0755))
{ perror("mkdir"); exit(EXIT_FAILURE); };
}
}
I hope you don't want to create a single directory named 1 2 3 4. It is possible and easy, but it really is poor taste. For your mental safety, use only letters, digits and underscores _ in directory names.
I am using snprintf(3) to convert an int to a character buffer. With C++11 you could use std::to_string and c_str ...
Read Advanced Linux Programming...
Using the mkdir(2) syscall instead of going thru a command invoked by system(3) has several important advantages:
it is much faster, you don't need to fork(2) a /bin/sh -c shell like system(3) should do.
it uses much less resources, since no additional process is fork-ed, so your program will still run when you have reached your limits (see setrlimit(2) ...)
it is more reliable. Should mkdir(2) fail you could (and should) handle the failure nicely. See errno(3) and strerror(3) ....

Why would I get a syntax error near unexpected token? Command line arguments?

Here is the code I have- not sure why I am getting this error message:
$ ./main.cpp "hello" "is"
./main.cpp: line 4: syntax error near unexpected token `('
./main.cpp: line 4: `int main(int argc, char *argv[]){'
It compiles fine in g++, but when I run it, I get the above error. Any idea why? Here is my complete code..
#include <iostream>
#include <fstream>
int main(int argc, char *argv[]){
for(int i = 0; i < argc; i++){
std::cout << argc << " : " << argv[i] << '\n';
}
if (argc != 2){
std::cout << "\nUSAGE: 2 command line arguments please." << std::endl;
std::cout << "\n (1) Input file with raw event scores.\n (2) Output file to write into.";
}
// open the font file for reading
std::string in_file = argv[1];
std::ifstream istr(in_file.c_str());
if (!istr) {
std::cerr << "ERROR: Cannot open input file " << in_file << std::endl;
}
return 0;
}
You have to run the compiled program, not the source code:
$ g++ -o main main.cpp
$ ./main "hello" "is"
3 : ./main
3 : hello
3 : is
USAGE: 2 command line arguments please.
   (1) Input file with raw event scores.
   (2) Output file to write into.ERROR: Cannot open input file hello
Your example is trying to execute C++ code as a shell script, which isn't going to work. As you can see from the output of my test run of your program here, you still have some bugs to work out.
As both the other answers say, you're running it as a shell script, implicitly using /bin/sh.
The first two lines starting with # are treated by the shell as comments. The third line is blank, and does nothing. The fourth line is interpreted as a command int, but parentheses are special to the shell, and are not being use correctly here. There probably isn't an int command in your $PATH, but the shell doesn't get a chance to report that because it chokes on the syntax error.
None of these details are particularly important; the problem is that you're executing the program incorrectly. But it might be interesting to see why those specific error messages are printed.
And it appears that you've done something like chmod +x main.cpp; otherwise the shell would have refused to try to execute it in the first place. Making a C++ source file executable isn't going to cause any real harm (as long as it's readable and writable), but it's not at all useful, and as you've seen it delayed the detection of your error. If you do chmod -x main.cpp, and then try ./main.cpp again, you'll get a "Permission denied" error instead.
As Carl's answer says, you need to execute the executable file generated by the compiler, not the C++ source file. That's why there's a compiler. The compiler (well, actually the linker) will automatically do the equivalent of chmod +x on the executable file it generates.
The file command will tell you what kind of file something is, which affects what you can do with it. For example, using your code on my system, after running g++ main.cpp -o main:
$ file main.cpp
main.cpp: ASCII C program text
$ file main
main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xacee0dfd9ded7aacefd679947e106b500c2cf75b, not stripped
$
(The file command should have recognized main.cpp as C++ rather than as C, but sometimes it guesses wrong.)
"ELF" is the executable format used by my system; the file contains executable machine code plus some other information. The pre-installed commands on the system use the same format.
The details may differ on your system -- and will differ substantially on non-Unix-like systems such as MS Windows. For example, on Windows executable files are normally named with a .exe extension.
The compiler, by default, creates an executable called "a.out", so you want to do:
$ a.out "hello" "is"
Typing "./main.cpp" is trying to execute the C++ source file, probably as a shell script

How to execute linux binary from MSYS

I wanted to try a program distributed in source for *nux on Windows. It's simple program with couple of C source files and headers
I have Mingw/Msys and first I tried to compile it there, by running make, but unfortunatelly I get obscure error (.text+0x175): undefined reference to 'strsep' which maight indicate a problem with autotools or similar, according Google search, but that's all about it I could get
As I mentioned that it is a simple program, I also tried to compile it with VS2008, until I figured it uses unix header file for parsing command line arguments, so nothing I can do there too
I launched my Ubuntu laptop and compiled the program in second, and tested it all fine, then I copy binary in my Windows Msys folder. No luck again as file can't be execeted (under Msys prompt of course) - it doesn't have execute flag and chmod on Msys has a "feature" that makes chmod +x unavailable
Update:
This is the error I get from msys prompt while trying to compile:
f77_diagram.c: In function 'handle_goto':
f77_diagram.c:57:4: warning: implicit declaration of function 'strsep' [-Wimplicit-function-declaration]
[...]
gcc -o f77_diagram block.o do_loop.o f77_diagram.o if.o label.o \
links.o parse_fortran.o
f77_diagram.o:f77_diagram.c:(.text+0x175): undefined reference to `strsep'
collect2: ld returned 1 exit status
That means that the function/variable strsep wasn't properly initialized. It most likely is a linking error. All it probably means is that you don't have a dependency installed. You'll need to post more information about the error.
By the way, you can't run linux binaries on windows.
Judging by your error, it looks like your compiler doesn't support the strsep function. To deal with this you will probably have to code your own, like this:
char *strsep(char **from, const char *delim) {
char *s, *dp, *ret;
if ((s = *from) == NULL)
return NULL;
ret = s;
while (*s != '\0') {
/* loop until the end of s, checking against each delimiting character,
* if we find a delimiter set **s to '\0' and return our previous token
* to the user. */
dp = (char *)delim;
while (*dp != '\0') {
if (*s == *dp) {
*s = '\0';
*from = s + 1;
return ret;
}
dp++;
}
s++;
}
/* end of string case */
*from = NULL;
return ret;
}
Here is the process that you will have to go through:
1- Find the file f77_diagram.c in the src directory.
2- Copy and paste the above code right after the #include statements.
3- Return to your command shell in the main make directory.
4- Re-run ./configure
5- Re-run make
If this doesn't work, there are a few other things you could try.
MSYS provides a Linux-like build environment for Windows. However, Linux binaries use the ELF or A.OUT formats, rather than the EXE format. Since there is nothing like WINE for Windows, Linux binaries have to be recompiled for Windows.

Problem with running c program on mac?

I'm a total beginner in C programming so please bear with me. I have just started today and wanted to write a short program - well at least a small script that would just print out a line of text. Now here's what I did in order to achieve this:
I downloaded vim text editor and wrote this few lines of code:
#include <stdio.h>
int main(void)
{
printf("This is some text written in C \n");
return 0;
}
I saved it as inform.c and compiled it using "cc inform.c" command.
In the end I got a.out file but when I'm trying to run it says:
-bash: a.out: command not found
Can someone tell what I'm doing wrong here and point me in the right direction? Thanks.
Bash can't find your command because the current directory is not usually in the path.
Try:
$ ./a.out
It's a basic one.
on Mac, you need to specify were your executable is.
when you type a.out, the system look for the command in /usr/bin and other synstem binaries folders.
to be more precise type ./a.out
which basically says : "in this directory, command a.out"
you should also add directly the classical signature of main which is :
int main(int argc, char ** argv);