How can I get the PID of a subshell in bash3.x? - bash

Is it possible to get $BASHPID value in bash3.x?
Actually, I cannot find any description about it in bash3.x man, but it's available in bash4.x.

You can try: bash -c 'echo $PPID'. This works for me on bash 4.1:
$ echo $$
8792
$ ( echo $BASHPID; echo $(bash -c 'echo $PPID') )
12987
12987

Related

Why does BASHPID change in here-strings

Can somebody explain the following behaviour with a here-string, please?
$ echo "$SHLVL $BASH_SUBSHELL $BASHPID $$"
1 0 18689 18689
$ cat <<< "$SHLVL $BASH_SUBSHELL $BASHPID $$"
1 0 19078 18689
$ cat <<< "$SHLVL $BASH_SUBSHELL $BASHPID $$"
1 0 19079 18689
The BASHPID is different from the shell PID in the here-string, and change each time, but I don't understand why.
The same happens in here-documents:
$ cat << EOT
> $SHLVL $BASH_SUBSHELL $BASHPID $$
> EOT
1 0 19096 18689
Surprisingly, BASHPID doesn't change in a command group:
$ { cat ;} <<< "$SHLVL $BASH_SUBSHELL $BASHPID $$"
1 0 18689 18689
On the other hand, it changes in a subshell
$ (echo $BASHPID ; cat) <<< "$SHLVL $BASH_SUBSHELL $BASHPID $$"
20465
1 1 20465 18689
while the here-string is supposed to expand in the current shell.
Note: my bash version is 4.3+
(Just guessing ...)
The behavior is similar to this:
# echo $$
35130
# echo $( echo $$ $BASHPID )
35130 88025
# echo $( echo $$ $BASHPID )
35130 88026
#
# # or
#
# echo $$ $BASHPID | cat
35130 88028
# echo $$ $BASHPID | cat
35130 88030
Apparently $BASHPID is not expanded at the same time as $$. According to man bash:
BASHPID
Expands to the process ID of the current bash process. This differs from $$ under certain circumstances, such as subshells that do not require bash to be re-initialized.
This implies $BASHPID is not expanded the time Bash parses the command line otherwise it'll be the same as $$. And in Bash source code there's a func initialize_dynamic_variables() (in file variables.c):
1905 static void
1906 initialize_dynamic_variables ()
1907 {
1908 SHELL_VAR *v;
1909
1910 v = init_seconds_var ();
1911
1912 INIT_DYNAMIC_VAR ("BASH_ARGV0", (char *)NULL, get_bash_argv0, assign_bash_argv0);
....
....
1924 INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
1925 VSETATTR (v, att_integer);
....
As it shows, vars like BASHPID are called dynamic variables. I guess these vars are handled specially and are expanded in the last minute when it knows that no more sub-shell will be forked (the fork may be followed by exec(), e.g., to run external commands).

how to pass arguments into xargs EOF

I try to use following command to sent out 4 spark jobs in parallel, and wait for all of them finished before starting new step. However I notice the $cmd_trainSparkModelx commands are empty inside 'xargs'. How to pass them into xargs?
eval $cmd_prepare_step
xargs -P 4 -I {} sh -c 'eval "$1"' - {} <<'EOF'
#eval "$cmd_trainSparkModel1"
#eval "$cmd_trainSparkModel2"
#eval "$cmd_trainSparkModel3"
#eval "$cmd_trainSparkModel4"
echo "$cmd_trainSparkModel1"
echo "$cmd_trainSparkModel2"
echo "$cmd_trainSparkModel3"
echo "$cmd_trainSparkModel4"
EOF
echo "finished training"
eval $cmd_postTraining_step
following command works. But would still like to see how to make xargs work, as it can also control how many jobs to run at the same time.
echo "$cmd_trainSparkModel1" &
p1=&!
echo "$cmd_trainSparkModel2" &
p2=&!
echo "$cmd_trainSparkModel3" &
p3=&!
echo "$cmd_trainSparkModel4" &
p4=&!
wait $p1 $p2 $p3 $p4

Escaping character from Windows to WSL

I am trying to send the following line :
trap 'test -n "$SSH_AUTH_SOCK" && eval `/usr/bin/ssh-agent -k`' 0
to a file from Windows to WSL.
Here is what I've got so far :
bash -c "echo -e 'trap test -n \"\$SSH_AUTH_SOCK\" && eval \`/usr/bin/ssh-agent -k\` 0' >> $HOME/test"
I can't find a way to quote the trap argument. Thanks in advence for your help.
A colleague of mine found a solution : use a fonction
bash -c "echo -e 'onexit() { test -n \"\$SSH_AUTH_SOCK\" && eval \`/usr/bin/ssh-agent -k\`; }' > $HOME/testA"
bash -c "echo -e trap onexit 0 >> $HOME/testA"

Alias for echo function with espeak executed in background

I want to replace the normal echo function in ubuntu bash with a function that additionally uses espeak to say something everytime echo is used.
I came up with an alias for my .bashrc
alias ghostTalk='espeak -v +whisper -s 80 -p 100 "$(myFun)"& /bin/echo $1'
(in my final version I would replace ghostTalk with echo)
But this gives as output:
~$ ghostTalk 123
[2] 5685
123
[1] Done espeak -v +whisper -s 80 -p 100 "$(myFun)"
How can I avoid this and have the normal echo output e.g. only 123 while its talking in the background?
Backgrounding notifications can be suppressed with a double-fork:
ghostTalk() {
( espeak -v +whisper -s 80 -p 100 "$(myFun)" & )
builtin echo "$#"
}

pgrep -P $$ gives an inexistent process id

#!/usr/bin/env bash
sleep 3 & # Spawn a child
trap '
pgrep -P $$ # Outputs one PID as expected
PIDS=( $( pgrep -P $$ ) ) # Saves an extra nonexistant PID
echo "PIDS: ${PIDS[#]}" # You can see it is the last one
ps -o pid= "${PIDS[#]:(-1)}" ||
echo "Dafuq is ${PIDS[#]:(-1)}?" # Yep, it does not exist!
' 0 1 2 3 15
It outputs
11800
PIDS: 11800 11802
Dafuq is 11802?
It only happens with traps.
Why is a nonexistent PID appended to the array? And how to avoid this odd behaviour?
By using $(...), you've created a subprocess which will execute that code.
Naturally, the parent of that process will be the current shell, so it's going to be listed.
As for the workaround, you could remove that PID from the list. First you have to know how to access the subshell PID: $$ in a script vs $$ in a subshell . Now you can filter it out (nope, it doesn't work):
PIDS=( $( pgrep -P $$ | grep -v ^$BASHPID$ ) )

Resources