Confusion when assigning a command-line statement to a Perl array [duplicate] - arrays

Can someone please help me? In Perl, what is the difference between:
exec "command";
and
system("command");
and
print `command`;
Are there other ways to run shell commands too?

exec
executes a command and never returns.
It's like a return statement in a function.
If the command is not found exec returns false.
It never returns true, because if the command is found it never returns at all.
There is also no point in returning STDOUT, STDERR or exit status of the command.
You can find documentation about it in perlfunc,
because it is a function.
system
executes a command and your Perl script is continued after the command has finished.
The return value is the exit status of the command.
You can find documentation about it in perlfunc.
backticks
like system executes a command and your perl script is continued after the command has finished.
In contrary to system the return value is STDOUT of the command.
qx// is equivalent to backticks.
You can find documentation about it in perlop, because unlike system and execit is an operator.
Other ways
What is missing from the above is a way to execute a command asynchronously.
That means your perl script and your command run simultaneously.
This can be accomplished with open.
It allows you to read STDOUT/STDERR and write to STDIN of your command.
It is platform dependent though.
There are also several modules which can ease this tasks.
There is IPC::Open2 and IPC::Open3 and IPC::Run, as well as
Win32::Process::Create if you are on windows.

In general I use system, open, IPC::Open2, or IPC::Open3 depending on what I want to do. The qx// operator, while simple, is too constraining in its functionality to be very useful outside of quick hacks. I find open to much handier.
system: run a command and wait for it to return
Use system when you want to run a command, don't care about its output, and don't want the Perl script to do anything until the command finishes.
#doesn't spawn a shell, arguments are passed as they are
system("command", "arg1", "arg2", "arg3");
or
#spawns a shell, arguments are interpreted by the shell, use only if you
#want the shell to do globbing (e.g. *.txt) for you or you want to redirect
#output
system("command arg1 arg2 arg3");
qx// or ``: run a command and capture its STDOUT
Use qx// when you want to run a command, capture what it writes to STDOUT, and don't want the Perl script to do anything until the command finishes.
#arguments are always processed by the shell
#in list context it returns the output as a list of lines
my #lines = qx/command arg1 arg2 arg3/;
#in scalar context it returns the output as one string
my $output = qx/command arg1 arg2 arg3/;
exec: replace the current process with another process.
Use exec along with fork when you want to run a command, don't care about its output, and don't want to wait for it to return. system is really just
sub my_system {
die "could not fork\n" unless defined(my $pid = fork);
return waitpid $pid, 0 if $pid; #parent waits for child
exec #_; #replace child with new process
}
You may also want to read the waitpid and perlipc manuals.
open: run a process and create a pipe to its STDIN or STDERR
Use open when you want to write data to a process's STDIN or read data from a process's STDOUT (but not both at the same time).
#read from a gzip file as if it were a normal file
open my $read_fh, "-|", "gzip", "-d", $filename
or die "could not open $filename: $!";
#write to a gzip compressed file as if were a normal file
open my $write_fh, "|-", "gzip", $filename
or die "could not open $filename: $!";
IPC::Open2: run a process and create a pipe to both STDIN and STDOUT
Use IPC::Open2 when you need to read from and write to a process's STDIN and STDOUT.
use IPC::Open2;
open2 my $out, my $in, "/usr/bin/bc"
or die "could not run bc";
print $in "5+6\n";
my $answer = <$out>;
IPC::Open3: run a process and create a pipe to STDIN, STDOUT, and STDERR
use IPC::Open3 when you need to capture all three standard file handles of the process. I would write an example, but it works mostly the same way IPC::Open2 does, but with a slightly different order to the arguments and a third file handle.

Let me quote the manuals first:
perldoc exec():
The exec function executes a system command and never returns-- use system instead of exec if you want it to return
perldoc system():
Does exactly the same thing as exec LIST , except that a fork is done first, and the parent process waits for the child process to complete.
In contrast to exec and system, backticks don't give you the return value but the collected STDOUT.
perldoc `String`:
A string which is (possibly) interpolated and then executed as a system command with /bin/sh or its equivalent. Shell wildcards, pipes, and redirections will be honored. The collected standard output of the command is returned; standard error is unaffected.
Alternatives:
In more complex scenarios, where you want to fetch STDOUT, STDERR or the return code, you can use well known standard modules like IPC::Open2 and IPC::Open3.
Example:
use IPC::Open2;
my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'some', 'cmd', 'and', 'args');
waitpid( $pid, 0 );
my $child_exit_status = $? >> 8;
Finally, IPC::Run from the CPAN is also worth looking at…

What's the difference between Perl's backticks (`), system, and exec?
exec -> exec "command"; ,
system -> system("command"); and
backticks -> print `command`;
exec
exec executes a command and never resumes the Perl script. It's to a script like a return statement is to a function.
If the command is not found, exec returns false. It never returns true, because if the command is found, it never returns at all. There is also no point in returning STDOUT, STDERR or exit status of the command. You can find documentation about it in perlfunc, because it is a function.
E.g.:
#!/usr/bin/perl
print "Need to start exec command";
my $data2 = exec('ls');
print "Now END exec command";
print "Hello $data2\n\n";
In above code, there are three print statements, but due to exec leaving the script, only the first print statement is executed. Also, the exec command output is not being assigned to any variable.
Here, only you're only getting the output of the first print statement and of executing the ls command on standard out.
system
system executes a command and your Perl script is resumed after the command has finished. The return value is the exit status of the command. You can find documentation about it in perlfunc.
E.g.:
#!/usr/bin/perl
print "Need to start system command";
my $data2 = system('ls');
print "Now END system command";
print "Hello $data2\n\n";
In above code, there are three print statements. As the script is resumed after the system command, all three print statements are executed.
Also, the result of running system is assigned to data2, but the assigned value is 0 (the exit code from ls).
Here, you're getting the output of the first print statement, then that of the ls command, followed by the outputs of the final two print statements on standard out.
backticks (`)
Like system, enclosing a command in backticks executes that command and your Perl script is resumed after the command has finished. In contrast to system, the return value is STDOUT of the command. qx// is equivalent to backticks. You can find documentation about it in perlop, because unlike system and exec, it is an operator.
E.g.:
#!/usr/bin/perl
print "Need to start backticks command";
my $data2 = `ls`;
print "Now END system command";
print "Hello $data2\n\n";
In above code, there are three print statements and all three are being executed. The output of ls is not going to standard out directly, but assigned to the variable data2 and then printed by the final print statement.

The difference between 'exec' and 'system' is that exec replaces your current program with 'command' and NEVER returns to your program. system, on the other hand, forks and runs 'command' and returns you the exit status of 'command' when it is done running. The back tick runs 'command' and then returns a string representing its standard out (whatever it would have printed to the screen)
You can also use popen to run shell commands and I think that there is a shell module - 'use shell' that gives you transparent access to typical shell commands.
Hope that clarifies it for you.

Related

Do exec() calls fork for each command if passed a shell script?

For each command in a shell script passed to exec(), is it forked and ran in a child process?
Say I have a shell script called test.sh with the following contents;
#!/bin/bash
echo Hello
echo There
What I want to know is how the series of commands is treated if I were to call execvp() on test.sh in a C program.
For each command within the script, is there a fork followed by another exec call on that command, before a return to the parent and a repeat for the next command?
So far I have used strace on this exact example. My findings are that if I put two echos into the script, there are no calls to clone() (which I believe equates to forking?), but if I put two separate cats as such:
#!/bin/bash
cat file1
cat file2
Then I find two calls to clone in the strace. At the same time, stracing a singular cat call on its own, without running it from an execvp call on a shell script, does not yield any clones in the strace.
I would really appreciate a clarification on the way which exec calls handle shell scripts.
The bash shell has a number of built-in commands that are executed in the same process as the shell. The echo command is one of those built in commands.
cat on the other hand is at external program, so the shell must fork and exec to create a process and run the program.
Some commands are built into the shell, so they don't need to fork. All the commands that implement control flow (e.g. if, while, case) have to be built in. So do the commands that change the shell process's state, such as cd and ulimit.
In addition, a number of simple commands are implemented as shell built-ins; these include echo and printf. So you won't see any forks in the first script with two echo commands.
The type command (itself a built-in) can be used to show which commands are built-ins. For example, type echo reports echo is a shell builtin, but type cat reports cat is /bin/cat.
Finally, the exit status of a script is the same as the exit status of the last command. So as an optimization, it doesn't fork for the last command, it simply execs it. That's why you see forks when the script has two cat commands, but not when there's only one.

execl + find + -exec: missing argument to `-exec'

I am trying to run execlp with find ... -exec ..., and the find program consistently tells me:
find: missing argument to `-exec'
What could be wrong? When I run find with these arguments on my shell, it succeeds.
My function calls follow (after looking at related SO threads, I have tried several arrangements of the argmuments):
execlp("find","find","/home/me","-exec","/usr/bin/stat", "{}", "\\;",NULL);
execlp("find","find","/home/me","-exec","/usr/bin/stat", "'{}'", "\\;",NULL);
execlp("find","find","/home/me","-exec","/usr/bin/stat", "{}", "';'",NULL);
execlp("find","find","/home/me","-exec","/usr/bin/stat {} \\;",NULL);
When you execute the command from C, you don't need the \ before ;
Using this syntax should work
execlp("find","find","/home/me","-exec","/usr/bin/stat", "{}", ";",NULL);
When on the shell, ; marks the end of a command, and has to be escaped. execlp doesn't go through the shell to execute a command, it does it immediately.
Edit -- you actually only do one find so this part is not necessary
Moreover, the exec family replaces the current process with the requested command. So only the first execlp will be executed.
A solution is to fork() for each find (one by one, waiting for the child process to end, otherwise the output will be a mix of all results).

Hijacking system("/bin/sh") to run arbitrary commands

I'm trying to perform a privilege escalation attack using a binary which performs the call:
system("/bin/sh");
Is there a way to pass commands as "arguments" or such with the opened shell?
(I don't see it opening, I guess it runs and dies as soon as it has nothing to do which is immediately).
Edit: I Cannot edit the code. It's compiled already.
If you execute
system("/bin/bash");
the shell enters into interactive mode. It reads commands from standard input and writes answers to standard output. The standard input and output is inherited from the calling (your) program. Your program will wait until the shell finishes (i.e. until you enter the command exit or you type ^D at the beginning of line). The shell will run with the same privileges as the calling program.
If you control stdin
What you'll need to do is connect stdin to something that will, when read, provide a source of commands before invoking that code.
I'm writing the below in bash, but you can convert it to whatever language you actually intend to do this in:
# create a file with the commands you want to run
cat >/tmp/commands <<'EOF'
echo "Hello world"
EOF
# open that file and copy its file descriptor to FD 0 (stdin)
exec </tmp/commands
# then invoke your compiled executable that starts a shell.
run-your-command-that-starts-a-shell
If the program controls or overrides its stdin
Another option is to pass ENV with the name of a file to source:
cat >/tmp/commands <<'EOF'
echo "Hello world"
EOF
ENV=/tmp/commands run-your-command-that-starts-a-shell

Safe version of popen()?

I use fork()/exec()/wait() rather than system() when the command has user input as some of its arguments so the user can't put something like...
&rm -rf /home/* && echo HAHA
... as an argument.
I'm assuming popen is as dangerous as system() because it takes a single string and not a list of strings like the exec family of functions do.
I can only get the return value from the exec functions though. Is there a "safe" version of popen that I can run with user input and process stdout / stderr back in the parent process?
The safe way is to set up the necessary pipes yourself, using straight pipe() calls directly.
That's what popen() does under the hood, but it also invokes the shell in order to run the child process. Skipping that step should make it safer.

using exec to sort a text file in c

I have a text file full of records (output.txt) and I want to sort every record by its id. After the sorting the sorted records are written into a new file (sorted.txt).
To do that I am using bash's command "sort" via an execl() function. To check the validity of my sort command, I wrote the same command straight into the bash and the result is the expected one. But when I try to use the execl command through my C program, most of the time the answer will be that there is not a file /usr/bin/sort (I am using Mac OSX) or no error message will be thrown but nevertheless nothing happens...
What I am using is this:
execl("/usr/bin/sort", "usr/bin/sort", "-n","-k", "1", "-u", "output.txt", ">", "sorted.txt", (char*)NULL);
or this
execl("/usr/bin/sort", "usr/bin/sort", "-n","-k", "1", "-u", "-o", "sorted.txt", "output.txt", (char*)NULL);
I know that both of these 2 sort commands are correct when I m using them in the bash. What happens to C?
Thnx all in advance!
Output redirection (> somefile.txt) is a feature of the shell, not the sort program (which AFAIK is not a bash built-in).
The exec family of functions doesn't start the shell, only the program you've specified.
If you don't know the path to the program, you can use the functions with p in their names (execlp in your case, I think) and just give them "sort" as program name, they'll search for it in $PATH like bash does.
Alternatively you can try system("sort output.txt > sorted.txt"). The system function's behaviour is implementation dependent though on linux it basically spawns a new shell which executes the command passed to it. system(ARG) is equivalent to sh -c ARG. The redirection will work if the shell supports it in your system's implementation of the system function.

Resources