What shell does std::system use? - c++

TL;DR; I guess the shell that std::system use, is sh. But, I'm not sure.
I tried to print the shell, using this code: std::system("echo $SHELL"), and the output was /bin/bash. It was weird for me. So, I wanted to see, what happens if I do that in sh? And, the same output: /bin/bash. Also, if I use a command like SHELL="/usr/bin/something", to set the SHELL variable to another string, it will print the new string that I set to it (/usr/bin/something), and it looks it's not a good way to see what shell it's using. Then, I tried to check it, using the ps command, and the output was: bash, a.out, ps. It was weird to see bash in this list. So, I created a custom shell, and change the shell in gnome-terminal to it:
#include <iostream>
int main()
{
std::string input;
while (true)
{
std::string command;
std::getline(std::cin, command);
std::system(command.c_str());
}
}
Now, it's easier to test, and I think, the results is better.
Then, I tried to test the ps command again, but in the custom shell, and the results was: test_shell, ps.
It was weird again. How the shell isn't sh, nor bash? And, the final test I did was: echo $0. And, the results was sh, in both custom shell, and normal program.
Edit
It seems like /bin/sh is linked to /bin/bash (ll /bin/sh command's output is /bin/sh -> bash), and actually, it seems like the only difference between sh and bash is filename, and the files's contents are the same. I checked the difference between these files with diff command too:
$ xxd /bin/sh > sh
$ xxd /bin/bash > bash
$ diff sh bash
(+ Yes, $SHELL doesn't means the running shell (I didn't know that when I was testing, and I just wanted to see what happens))

The GNU sources (https://github.com/lattera/glibc/blob/master/sysdeps/posix/system.c) say
/bin/sh
So, whatever /bin/sh is hardlinked to is the shell invoked by std::system() on Linux.
(This is correct, as /bin/sh is expected to be linked to a sane shell capable of doing things with the system.)

According to cppreference.com, std::system
calls the host environment's command processor (e.g. /bin/sh, cmd.exe, command.com)
This means the shell used will depend on the operating system.
On any POSIX OS (including Linux), the shell used by std::system is /bin/sh. (Though as the OP points out, /bin/sh could be a symlink to another shell.)
As for the SHELL environment variable, as has been pointed out in the comments, this environment variable cannot be used to reliably identify the running shell program. SHELL is defined by POSIX to
represent a pathname of the user's preferred command language interpreter
(source)

Related

system() call NOT targeting a sub-shell?

Working on a C++ Unix program executed on the command line (MacOs).
I call system("history -s SOMETHING") in it to add SOMETHING to the history of the user's shell, but I guess the call is opening a new sub-shell.
My question is : can I execute the system call on the "current" shell (the one used to run the program) ?
To be clear I want to find the SOMETHING in my shell history when I quit the program.
Thanks !
As far as I know, it's not possible in general.
If you're using bash, and since this is only for you:
Enable history appending in .bashrc:
shopt -s histappend
Launch a login bash shell in main:
system("bash -li -c 'history -s SOMETHING'");
and then refresh your history:
history -n
The history -n can be automated - you can execute it inside your prompt, for instance.
Figuring out how to do that left as an exercise.
(Disclaimer: I have only tried this in Ubuntu under the Windows Subsystem for Linux, but it should work very similarly on a Mac.)
It isn't possible. The usual work around -- not applicable if there is other wanted output -- is to make your program prints the wanted command and then execute it. For instance
#include <iostream>
int main() {
std::cout << "history -s SOMETHING\n";
return 0;
}
and then
eval $(/path/to/my/exe)
For ease of use, you can put that in a shell function
myfn() {
eval $(/path/to/my/exe)
}
that you can simply use
myfn

system("history") not working

I've run into a snag, I'm trying to implement a linux shell program of sorts with C++ and many of my commands seem to work, however, when I try to get the history(list all recently executed commands) I get an error of "sh: 1: history: not found" the below line is all that runs in the area, what is the issue?
system("history"); //produces the error above ^
If I do
$ history
from the command line it's fine...why is it not fine in C++?
system executes a program using /bin/sh, but history is a bash builtin.
You might look at the contents of ~/.bash_history instead. (Note (by leemes) .bash_history is only updated after closing a previous bash session, as well as it is not updated by executing a command with system.)
Because it's a bash shell builtin not necessarily accessible through /bin/sh -c` (which may be the bourne shell).

How to work in batch mode

I have inherited an ANSI C++ program that: has no GUIs and is supposed to run in batch mode, generating lots of data (we are talking 100,000+ ASCII files). We are thinking that in long term we’ll run it under UNIX. For now, I have a MacBook Air running OS X 10.9.4 and I loaded Xcode 5.1.1. It compiles without errors or warnings.
I need to test a program as follows:
<prompt> myprogram datain dataout1 datout2
Where is the compiled program? In which directory? Can I copy my datain file in that directory?
For repeated execution under Windows (Command Prompt window) I normally would have a batch file of the type:
myprogram datain1 dataout11 datout12
myprogram datain2 dataout21 datout22
myprogram datain3 dataout31 datout32
........
myprogram datainn dataoutn1 datoutn2
Can I do something similar with OS X? Where can I find the applicable documentation?
You will want to look for your terminal emulation program. See http://en.wikipedia.org/wiki/Terminal_(OS_X) for how to use it, and it should be the bash shell which is one of the unix shells
You can also do a shell script see
http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html for some bash shell scripting info
For such a simple operation, you can write a shell script that will look almost exactly the same as the batch file you use on Windows. The key difference between Windows' cmd.exe and *nix shells here is that the current directory is not part of the search path for executables (the way it is on Windows), so if you put the shell script in the same folder as the compiled executable, you will need to prefix the program name with ./ (to mean "look in the current directory"). For example:
#!/bin/sh
./myprogram datain1 dataout11 datout12
./myprogram datain2 dataout21 datout22
./myprogram datain3 dataout31 datout32
........
./myprogram datainn dataoutn1 datoutn2
If the shell script and executable are not in the same folder, you can use either an absolute path or an appropriate relative path.
Also, to run the script you will either need to make it executable:
$ chmod +x myscript.sh
$ ./myscript.sh
or invoke the shell with the script as an argument:
$ sh myscript.sh

Output of C++ function system(command) does not show color in Linux terminal

When I directly run a command in my Linux terminal, say "ls", the output is with color. However, when I run a C++ program which calls system("ls"), the output does not have color.
Is there way to get the latter way to also display colored output?
Thanks!
The answer for why there's no color lies here.
system() executes a command specified in command by calling /bin/sh -c
command, and returns after the command has been completed.
sh -c ignores aliases. Perhaps somewhere you have an alias where ls means ls --color=auto.
So for example, if I do sh -c 'ls', I will get no color.
Proof:
wow ♪[01:04 AM][vnbraun#chernobyl ~]$ which ls
alias ls='ls --color=auto'
/bin/ls
wow ♪[01:08 AM][vnbraun#chernobyl ~]$ sh -c 'which ls'
/bin/ls
Therefore, you can try doing system("ls --color=auto");.
You could run
system("/bin/ls --color=auto");
But I don't think you really should run ls from your C++ program. Perhaps you want to use -some combination of- readdir(3), stat(2), nftw(3), glob(3), wordexp(3) etc etc....
I don't think that forking a shell which then runs /bin/ls is useful from a C++ program. There are simpler ways to achieve your goal (which I cannot guess).
You probably should read Advanced Linux Programming
Try invoking ls --color=auto or ls --color=always to display ls with colors.
This is likely due to a bash configuration file somewhere in your system aliasing "ls" to "ls --color".
Using "ls --color" in your program should work.

Use GDB to debug a C++ program called from a shell script

I have a extremely complicated shell script, within which it calls a C++ program I want to debug via GDB. It is extremely hard to separate this c++ program from the shell since it has a lot of branches and a lot of environmental variables setting.
Is there a way to invoke GDB on this shell script? Looks like gdb requires me to call on a C++ program directly.
In addition to options mentioned by #diverscuba23, you could do the following:
gdb --args bash <script>
(assuming it's a bash script. Else adapt accordingly)
There are two options that you can do:
Invoke GDB directly within the shell script. This would imply that you don't have standard in and standard out redirected.
Run the shell script and then attach the debugger to the already running C++ process like so: gdb progname 1234 where 1234 is the process ID of the running C++ process.
If you need to do things before the program starts running then option 1 would be the better choice, otherwise option 2 is the cleaner way.
Modify the c++ application to print its pid and sleep 30 seconds (perhaps based on environment or an argument). Attach to the running instance with gdb.
I would probably modify the script to always call gdb (and revert this later) or add an option to call gdb. This will almost always be the easiest solution.
The next easiest would be to temporarily move your executable and replace it with a shell script that runs gdb on the moved program. For example, in the directory containing your program:
$ mv program _program
$ (echo "#!/bin/sh"; echo "exec gdb $PWD/_program") > program
$ chmod +x program
Could you just temporarily add gdb to your script?
Although the answers given are valid, sometimes you don't have permissions to change the script to execute gdb or to modify the program to add additional output to attach through pid.
Luckily, there is yet another way through the power of bash
Use ps, grep and awk to pick-out the pid for you after its been executed. You can do this by either wrapping the other script with your own or by just executing a command yourself.
That command might look something like this:
process.sh
#!/usr/bin/env bash
#setup for this example
#this will execute vim (with cmdline options) as a child to bash
#we will attempt to attach to this process
vim ~/.vimrc
To get gdb to attach, we'd just need to execute the following:
gdb --pid $(ps -ef | grep -ve grep | grep vim | awk '{print $2}')
I use ps -ef here to list the processes and their arguments. Sometimes, you'll have multiple instances of a program running and need to further grep down to the one you want
the grep -ve grep is there because the f option to ps will include the next grep in its list. If you don't need the command arguments for additional filtering, don't include the -f option for ps and ignore this piece
grep vim is where we're finding our desired process. If you needed more filtering, you could just do something like grep -E "vim.*vimrc" and filter down to exactly the process that you're trying to attach to
awk '{print $2}' simply outputs just the process' pid to stdout. Use $1 if you're using ps -e instead of ps -ef
My normal setup is to run such script that starts my process in 1 tmux pane and having typed something similar to the above in a bottom pane. That way if I need to adjust the filtering (for whatever reason), I can do it pretty quickly.
Usually though, it will be the same for a specific instance and I want to just attach automatically after its been started. I'll do the following instead:
runGdb.py
#!/usr/bin/env bash
./process.sh &
PID=$(ps -ef | grep -ve grep | grep -E "vim.*vimrc" | awk '{print $2}')
#or
#PID=$(ps -e | grep vim | awk '{print $1}')
gdb --pid $PID
This assumes that the original process can be safely run in the background.