using system() in C++;does not work - c++

I need to execute this command in my c++ code:
mkdir -p sample_directory/{1..10}
to make 10 directory . But when I use it in system function in my code:
system("mkdir -p sample_director/{1..10}");
after execution this make just one directory by this name :{1..10}
how can i fix it?

Brace expansion is not in POSIX, so not all shells implement it. In particular, in POSIX, system is supposed to invoke sh, which is supposed to act like a "plain" POSIX shell rather than bash or some other feature-rich shell. So it won't do the brace expansion.
You could directly invoke bash if you want bash to process the command...
system("bash -c 'mkdir -p sample_director/{1..10}'");
or you could just make 10 separate system calls. Or other workarounds, I'm sure you can think of some.

You can probably try to do the same using a loop construct.
# include <cstdlib>
# include <string>
using namespace std;
int main(){
for(int i=1;i<=10;i++){
string str="mkdir -p sample_director/" + to_string(i);
system(str.c_str());
}
return 0;
}
And run your code with c++11 support
$ g++ -std=c++11 my_program.cpp

It doesn't work because dash doesn't support brace expansion (syntax like {1..10}). The system function doesn't care about the value of your SHELL, it always passes provided arguments to /bin/sh. And on Ubuntu /bin/sh refers to dash, which cannot handle brace expansion. On some other distributions (like Arch), /bin/sh is a symlink to bash, so your code will work fine.
If I were you, I wouldn't use system at all in this case. Boost::filesystem (or even mkdir/mkdirat) seems like a better option to me.

Related

/bin/sh: 1: Syntax error: "(" unexpected after running Makefile [duplicate]

I often find Bash syntax very helpful, e.g. process substitution like in diff <(sort file1) <(sort file2).
Is it possible to use such Bash commands in a Makefile? I'm thinking of something like this:
file-differences:
diff <(sort file1) <(sort file2) > $#
In my GNU Make 3.80 this will give an error since it uses the shell instead of bash to execute the commands.
From the GNU Make documentation,
5.3.2 Choosing the Shell
------------------------
The program used as the shell is taken from the variable `SHELL'. If
this variable is not set in your makefile, the program `/bin/sh' is
used as the shell.
So put SHELL := /bin/bash at the top of your makefile, and you should be good to go.
BTW: You can also do this for one target, at least for GNU Make. Each target can have its own variable assignments, like this:
all: a b
a:
#echo "a is $$0"
b: SHELL:=/bin/bash # HERE: this is setting the shell for b only
b:
#echo "b is $$0"
That'll print:
a is /bin/sh
b is /bin/bash
See "Target-specific Variable Values" in the documentation for more details. That line can go anywhere in the Makefile, it doesn't have to be immediately before the target.
You can call bash directly, use the -c flag:
bash -c "diff <(sort file1) <(sort file2) > $#"
Of course, you may not be able to redirect to the variable $#, but when I tried to do this, I got -bash: $#: ambiguous redirect as an error message, so you may want to look into that before you get too into this (though I'm using bash 3.2.something, so maybe yours works differently).
One way that also works is putting it this way in the first line of the your target:
your-target: $(eval SHELL:=/bin/bash)
#echo "here shell is $$0"
If portability is important you may not want to depend on a specific shell in your Makefile. Not all environments have bash available.
You can call bash directly within your Makefile instead of using the default shell:
bash -c "ls -al"
instead of:
ls -al
There is a way to do this without explicitly setting your SHELL variable to point to bash. This can be useful if you have many makefiles since SHELL isn't inherited by subsequent makefiles or taken from the environment. You also need to be sure that anyone who compiles your code configures their system this way.
If you run sudo dpkg-reconfigure dash and answer 'no' to the prompt, your system will not use dash as the default shell. It will then point to bash (at least in Ubuntu). Note that using dash as your system shell is a bit more efficient though.
It's not a direct answer to the question, makeit is limited Makefile replacement with bash syntax and it can be useful in some cases (I'm the author)
rules can be defined as bash-functions
auto-completion feature
Basic idea is to have while loop in the end of the script:
while [ $# != 0 ]; do
if [ "$(type -t $1)" == 'function' ]; then
$1
else
exit 1
fi
shift
done
https://asciinema.org/a/435159

What shell does std::system use?

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)

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

using system() to call ln from a cpp program

I am trying to call system from a cpp program with the following command
system("ln -s -t \"targetDir[0]\" \"baseDir[0]\"");
Both targetDir and baseDir are QStringList. The program compiles and runs but when I execute the command i get the error ln : targetDir[0] is an invalid command. When I test by hard coding the values instead of using variables it works just fine. I can only conclude it is not escaping the string to put the value of the variables int the argument passed to ln. For the life of me I can't figure out why not.
Any Ideas?
You are confused. The system(3) library function (it is not a command, and despite its name is not a system call, those are listed in syscalls(2)) is forking a /bin/sh -c process which obviously don't have any idea about the variables of your C++ program (at runtime, variables don't exist; there are only locations).
BTW, using system(3) without care can be dangerous because of code injection issues. Imagine in your (wrong) approach that the targetDir[0] contains something like foo; rm -rf $HOME ....
To make a symbolic link, forking a process is overkill. Just call the symlink(2) system call (which the ln(1) command will call if invoked as ln -s)
The Qt library offers the QFile class with its QFile::link member function, or the static QFile::link (both will call symlink(2))
Future (or recent) versions of C++, starting from C++17, will provide the std::filesystem::create_symlink function (which on Linux will call
symlink(2)). It is probably inspired by Boost filesystem library.
PS. If coding for Linux or POSIX I recommend reading Advanced Linux Programming (which is freely downloadable). But if you want a source-portable Qt program restrict yourself to the generous Qt API. Or adopt C++17 and use its std::filesystem thing.
C++ by no means performs string interpolation.
If you are actually writing in C++, you can (considering targetDir is char ** or something alike):
std::string command = std::string("ln -s -t \"") + targetDir[0] + "\" \"" + baseDir[0] + "\"";
system(command.c_str());

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) ....