command substitution in RUBY - ruby

Please explain the Ruby code below, I can't able to understand the code.
In command_substitution.rb
a = %x!ps -def |grep bash!
puts a
Output
1000 3806 3799 0 10:54 pts/0 00:00:00 -bash
1000 4981 4979 0 12:50 pts/0 00:00:00 sh -c ps -def |grep bash
1000 4984 4981 0 12:50 pts/0 00:00:00 grep bash
I searched to find out what is command substitution using Google. But, I didn't get the exact explanation about it.
Please explain .

The %x notation is a literal for executing a shell script. Here, the character ! is used to indicate the start and the end of the literal. It runs the command ps -def |grep bash in the shell, assigns the result to variable a, and prints it.
Within the shell script, ps gets the running processes, | passes that to the next command, which is grep, which searches for the string bash within the processes shown.

Related

Executing a shell with a command and returning

man bash seems to suggest that if I want to execute a command in a separate bash shell all I have to do is bash -c command:
-c string If the -c option is present, then commands are read from string.
I want to do that because I need to run a few things in different environments:
bash --rcfile ~/.bashrc.a -c mytest.a
bash --rcfile ~/.bashrc.b -c mytest.b
However, that didn't work as expected; one can see that by the number of bash shells running, for example:
$ bash
$ ps
PID TTY TIME CMD
7554 pts/0 00:00:00 bash
7573 pts/0 00:00:00 ps
28616 pts/0 00:00:00 bash
$ exit
exit
$ ps
PID TTY TIME CMD
7582 pts/0 00:00:00 ps
28616 pts/0 00:00:00 bash
$ bash -c ps
PID TTY TIME CMD
7583 pts/0 00:00:00 ps
28616 pts/0 00:00:00 bash
How should the invocation of bash should be modified so that it would start a new shell with the specified rc, execute the given command in that shell (with the env modified according to the rc), and exit back?
It's already working exactly the way you want it to. The lack of an extra process is simply due to bash's tail-call optimization.
Bash recognizes that there's no point in having a shell instance whose only job is to wait for a process and exit. It will instead skip the fork and exec the process directly. This is a huge win for e.g. var=$(ps), where it cuts the number of expensive forks from 2 to 1.
If you give it additional commands to run afterwards, this optimization is no longer valid, and then you'll see the additional process:
$ bash -c 'ps'
PID TTY TIME CMD
4531 pts/10 00:00:00 bash
4540 pts/10 00:00:00 ps
$ bash -c 'ps; exit $?'
PID TTY TIME CMD
4531 pts/10 00:00:00 bash
4549 pts/10 00:00:00 bash
4550 pts/10 00:00:00 ps
bash --rcfile ~/.bashrc.a mytest.a will already run mytest.a in a separate process. -c is for specifying a shell command directly, rather than running a script.
# NO!
bash for x in 1 2 3; do echo "$x"; done
# Yes.
bash -c 'for x in 1 2 3; do echo "$x"; done'

Everytime I run ps, it returns the usual PID and CMD, but

In every book I've read, it never returns like this:
PID CMD
2748 -bash
8114 awk
7900 -bash
Which is what my ps returns. Is that normal for the - to be in front of the bash? I've only ever seen 2290 bash, never without the - in front of it. Trivial question, but I assume it isn't normal. Thank you, and sorry for the stupid question.
This means a login shell. Take a look at man bash:
A login shell is one whose first character of argument zero is a -, or one started with the --login option.
If you run cat /proc/2748/cmdline you will see the hyphen there. This is where ps is getting it from.
-f will look at /proc/[pid]/cmdline, whereas by default it will look at /proc/[pid]/comm.
tom#riki:~$ ps
PID TTY TIME CMD
9230 pts/2 00:00:00 bash
9429 pts/2 00:00:00 ps
tom#riki:~$ ps -f
UID PID PPID C STIME TTY TIME CMD
tom 9230 9229 0 17:39 pts/2 00:00:00 -bash
tom 9427 9230 0 18:22 pts/2 00:00:00 ps -f
tom#riki:~$ cat /proc/9230/comm
bash
tom#riki:~$ cat /proc/9230/cmdline
-bash

Retrieving full command line (w/ pipes &c) from a running bash script

How can I get the complete line of code running in the bash in a script that is run from within this line?
ping -c 2 google.com & ping -c 2 aol.com | grep aol & sh myscript.sh
where I want to retrieve the complete upper line in myscript.sh somehow.
My current approach is:
ping -c 2 google.com & ping -c 2 aol.com | grep aol & ps -ef --sort=start_time
And then correlate the PPID and the start time of the process to get what was run.
UID PID PPID C STIME TTY TIME CMD
nm+ 2881 6599 0 12:09 pts/1 00:00:00 ping -c 2 google.com
nm+ 2882 6599 0 12:09 pts/1 00:00:00 ping -c 2 aol.com
nm+ 2883 6599 0 12:09 pts/1 00:00:00 grep --color=auto abc
nm+ 2884 6599 0 12:09 pts/1 00:00:00 ps -ef --sort=start_time
I dont like it since I am unable to say how the processes are connected (pipes or just parallel execution) and therefore its impossible to reconstruct the exact line that was run in the bash. Also it feels to hackish for the right way.
You can grep "pipe" from lsof and find the correlated commands from the pipe id for a process and find the process id and look for details for the correlated processes.
Assuming bash 4.0 or newer:
#!/usr/bin/env bash
exec 3>"$1"; shift
BASH_XTRACEFD=3 PS4=':$BASH_SOURCE:$LINENO:+'
set -x
source "$#"
...if saved as bash_trace, used as:
bash_trace logfile scriptname arg1 arg2 ...
...then, to look up the actual line number, one can use something like the following:
IFS=: read -r filename lineno _ < <(tail -n 1 logfile)
sed -e "${lineno}q;d" <"$filename"

Why does echo $$ return a number? [duplicate]

This question already has answers here:
What are the special dollar sign shell variables?
(4 answers)
Closed 8 years ago.
Why do I get a number when doing that :
echo $$
which returns
489
If I open a new terminal it returns another number. It seems it's related to the pid of the terminal session, but why ?
$$ means your current PID.
As seen in Bash Reference Manual - 3.4.2 Special Parameters:
$
Expands to the process ID of the shell. In a () subshell, it expands
to the process ID of the invoking shell, not the subshell.
You can test it by doing ps -ef | grep 489, and it will show the process in which you are logged in.
For example in my case:
$ echo $$
3470
$ ps -ef | grep 3470
1000 3470 3469 0 10:59 pts/3 00:00:00 -bash <---- this process
1000 8151 3470 0 15:37 pts/3 00:00:00 ps -ef
1000 8152 3470 0 15:37 pts/3 00:00:00 grep --color=auto 3470
Because that's how it is defined. $$ is a special shell variable (like e.g. $!, $_, $#, $1, ...) referring to the PID of the invoked shell.
You will find an excellent explanation in this post.
$$ pid of the current shell (not subshell)

Can't understand ps | wc output differences

I was trying to write a set of functions that could check to see if a process name was running when I encountered some unexpected output. I've condensed the issue in the following script names isRunning.sh which depends on a system ps command that can take the '-fC' arguments...
#!/bin/bash
progname=isRunning.sh
ps -fC isRunning.sh
pRet=`ps -fC ${progname} | wc -l`
echo pRet $pRet
psOut=`ps -fC ${progname}`
wcOut=`echo "${psOut}" | wc -l`
echo
echo ps output
echo "${psOut}"
echo
echo wcOut $wcOut
The first attempt at piping the ps output to wc gets a return of 3. The second attempt gets the expected return value of 2. Can anyone explain this behavior? I figure it's got to be something stupid I am overlooking.
Thanks,
bbb
edit: my output
UID PID PPID C STIME TTY TIME CMD
root 6717 5940 0 13:10 pts/0 00:00:00 /bin/bash ./isRunning.sh
pRet 3
ps output
UID PID PPID C STIME TTY TIME CMD
root 6717 5940 0 13:10 pts/0 00:00:00 /bin/bash ./isRunning.sh
wcOut 2
I get 2 both attempts. Your ps might be outputting an extra blank line, or somesuch, and then your shell's backtick expansion stripping it. Or maybe you actually had two processes matching the first time you ran it.
If you just want to see if its running, check the exit code from your ps:
if ps -C "${progname}" > /dev/null; then
echo its running
else
echo not running
fi
Even better, you should take a look at pidof and pgrep if you can rely on them being present on whichever systems you're targeting. Or use the LSB functions, if you're on Linux.
edit: Actually, since you're looking for copies of yourself running, you might be picking up the shell doing a fork to implement the pipe.

Resources