Bash auto increment in operation - bash

Please explain this problem with incrementing value, we have next file named test.sh
#!/bin/bash
ps aux | grep test.sh -c
echo $(ps aux | grep test.sh -c)
and then run it
$ ./test.sh
2
3
I know there is two lines after grep slice (1 with test.sh, 2 with grep), why 3 come in? Thanks

You get 3 in the second case because the second command $(...) (i.e. command substitution) executes in a subshell.
From the manual:
Command substitution, commands grouped with parentheses, and
asynchronous commands are invoked in a subshell environment that is a
duplicate of the shell environment, ...

Related

Bash get the command that is piping into a script

Take the following example:
ls -l | grep -i readme | ./myscript.sh
What I am trying to do is get ls -l | grep -i readme as a string variable in myscript.sh. So essentially I am trying to get the whole command before the last pipe to use inside myscript.sh.
Is this possible?
No, it's not possible.
At the OS level, pipelines are implemented with the mkfifo(), dup2(), fork() and execve() syscalls. This doesn't provide a way to tell a program what the commands connected to its stdin are. Indeed, there's not guaranteed to be a string representing a pipeline of programs being used to generate stdin at all, even if your stdin really is a FIFO connected to another program's stdout; it could be that that pipeline was generated by programs calling execve() and friends directly.
The best available workaround is to invert your process flow.
It's not what you asked for, but it's what you can get.
#!/usr/bin/env bash
printf -v cmd_str '%q ' "$#" # generate a shell command representing our arguments
while IFS= read -r line; do
printf 'Output from %s: %s\n' "$cmd_str" "$line"
done < <("$#") # actually run those arguments as a command, and read from it
...and then have your script start the things it reads input from, rather than receiving them on stdin.
...thereafter, ./yourscript ls -l, or ./yourscript sh -c 'ls -l | grep -i readme'. (Of course, never use this except as an example; see ParsingLs).
It can't be done generally, but using the history command in bash it can maybe sort of be done, provided certain conditions are met:
history has to be turned on.
Only one shell has been running, or accepting new commands, (or failing that, running myscript.sh), since the start of myscript.sh.
Since command lines with leading spaces are, by default, not saved to the history, the invoking command for myscript.sh must have no leading spaces; or that default must be changed -- see Get bash history to remember only the commands run with space prefixed.
The invoking command needs to end with a &, because without it the new command line wouldn't be added to the history until after myscript.sh was completed.
The script needs to be a bash script, (it won't work with /bin/dash), and the calling shell needs a little prep work. Sometime before the script is run first do:
shopt -s histappend
PROMPT_COMMAND="history -a; history -n"
...this makes the bash history heritable. (Code swiped from unutbu's answer to a related question.)
Then myscript.sh might go:
#!/bin/bash
history -w
printf 'calling command was: %s\n' \
"$(history | rev |
grep "$0" ~/.bash_history | tail -1)"
Test run:
echo googa | ./myscript.sh &
Output, (minus the "&" associated cruft):
calling command was: echo googa | ./myscript.sh &
The cruft can be halved by changing "&" to "& fg", but the resulting output won't include the "fg" suffix.
I think you should pass it as one string parameter like this
./myscript.sh "$(ls -l | grep -i readme)"
I think that it is possible, have a look at this example:
#!/bin/bash
result=""
while read line; do
result=$result"${line}"
done
echo $result
Now run this script using a pipe, for example:
ls -l /etc | ./script.sh
I hope that will be helpful for you :)

Expansion of variable does not work when calling bash functions

See also my previous question.
So... I have a script:
function go_loop (){
for i in `grep -v ^# $1`; do
$2
done
}
go_loop "/tmp/text.txt" "echo $i"
I should have in a result:
9
20
21
...
But apparently I only get an empty result. How can I feed the second input parameter to the loop?
Please don't advice me do this:
for i in `grep -v ^# $1`; do
echo $i
done
I need to make 2 input parameters, first - name of file, second - name of execution command
You need to eval the second parameter like this:
eval $2
and pass it like this:
go_loop "/tmp/text.txt" 'echo $i'
You can do this using exec inside your loop, which will run the $2 as bash command:
[root#box ~]# ./test.sh 1 ls
test.sh tests_passed.txt
[root#box ~]# cat test
exec $2
The exec builtin command is used to
replace the shell with a given program (executing it, not as new process)
set redirections for the program to execute or for the current shell

Getting chained/piped commands result to shell variable

Given a db2 proc call's output:
$ db2 "call SOME_PROC(103,5,0,'','',0,0)"
Return Status = 0
I wish to just get the value and when I 'chain-em-up' it does not work as I think it should, so given:
$ db2 "call SOME_PROC(103,5,0,'','',0,0)" | sed -rn 's/ *Return Status *= *([0-9]+)/\1/p'
0
I try to chain 'em up:
$ var=$(db2 "call SOME_PROC(103,5,0,'','',0,0)" | sed -rn 's/ *Return Status *= *([0-9]+)/\1/p')
$ echo $var
You get nothin !
But if you redirect to tmp file:
$ db2 "call SOME_PROC(103,5,0,'','',0,0)" > /tmp/fff
$ var=$(cat /tmp/fff | sed -rn 's/ *Return Status *= *([0-9]+)/\1/p')
$ echo $var
0
You do get it ...
Similarly if you put in var:
$ var=$(db2 "call DB2INST1.USP_SPOTLIGHT_GET_DATA(103,5,0,'','',0,0)")
$ var=$(echo $var | sed -rn 's/ *Return Status *= *([0-9]+)/\1/p')
$ echo $var
0
You also get it ...
Is there a way to get value as my first attempt? And also I wonder why does it not work? What am I missing?
I also tried the below and it also givs nothing!
cat <(db2 -x "call DB2INST1.USP_SPOTLIGHT_GET_DATA(103,5,0,'','',0,0)" | sed -rn 's/ *Return Status *= *([0-9]+)/\1/p')
The db2 command-line interface requires that the db2 command be issued as a direct child of the parent of the command which initiated the connection. In other words, the db2 call and db2 connect commands need to be initiated from the same shell process.
That does not interact well with many shell features:
pipelines: cmd1 | cmd2 runs both commands in subshells (different processes).
command substitution: $(cmd) runs the command in a subshell.
process substitution (bash): <(cmd) runs the command in a subshell.
However, if the shell is bash, the situation is not quite that restricted, so under some circumstances the above constructions will still work with db2. In pipelines and command substitution, bash will optimize away the subshell if the command to be run in the subshell is simple enough. (Roughly speaking, it must be a simple command without redirects.)
So, for example, if some bash process P executes
cmd1 | cmd2
then both commands have P as their parent, because they are both simple commands. Similarly with
a=$(cmd)
However, if a pipelined command or a substituted command is not simple, then a subshell is required. For example, even though { ...} does not require a subshell, the syntax is not a simple command. So in
{ cmd1; } | cmd2
the first command is a child of a subshell, while the second command is a child of the main shell.
In particular, in
a=$(cmd1 | cmd2)
bash will not optimize away the command-substitution subshell, because cmd1 | cmd2 is not a simple command. So it will create a subshell to execute the pipeline; that subshell will apply the optimization, so there will not be additional subshells to execute the simple commands cmd1 and cmd2.
In short, you can pipeline the output of db2 call or you can capture the output in a variable, but you cannot capture the output of a pipeline in a variable.
Other shells are more (or less) capable of subshell optimizations. With zsh, for example, you can use process substitution to avoid the subshell:
# This will work with zsh but not with bash
var=$(db2 "call ..." > >(sed -rn ...))
The syntax cmd1 > >(cmd2) is very similar to the pipeline cmd1 | cmd2, but it differs in that is syntactically a simple command. For zsh, that is sufficient to allow the elimination of the subshell (but not for bash, which won't optimize away a subshell if the command involves a redirection).
As #rici so briliantly explained it all, I just wanna show it live:
With cmd | cmd you get:
$ db2 "call SOME_PROC(103,5,0,'','',0,0)" | cat
Return Status = 0
But with {cmd ;} | cmd you get:
$ { db2 "call SOME_PROC(103,5,0,'','',0,0)" ;} | cat
SQL1024N A database connection does not exist. SQLSTATE=08003

Bash script to run last command with valgrind

I am pretty inexperienced with bash. I am trying to save the last run command as a variable, this is what I have:
#!/bin/bash
prev=$(fc -ln -1)
echo $prev
This doesn't print anything. Once I save the last command, I plan on coding this line:
valgrind --leak-check=full $prev
So what am I doing wrong?
fc references the history of the current shell. When run inside a shell script it refers to history of that new shell. If you use an alias or shell function, then fc will operate within the current shell. You can also source a script file for the same effect.
$ cat go
#!/bin/bash
set -o history
echo a b c
fc -nl -1
$ ./go
a b c
echo a b c
$ alias zz='fc -nl -1 | tr a-z A-Z'
$ zz
ALIAS ZZ='FC -NL -1 | TR A-Z A-Z'
I'd use aliases rather than a script, something like:
alias val='valgrind --leak-check=full'
alias vallast='val $(history 2 | head -1 | cut -d" " -f3-)'
As explained in the link, you can add these lines to your .bashrc.
BTW, the latter can also be executed as:
val !!
val !-1 #same
Or if you want to valgrind the program you ran 2 commands ago:
val !-2
These history commands are explained here.

Input parameter $$ .sh script

I would like to ask that what is the meaning of $$ in a .sh script.
My program:
#!/bin/sh
V1=$1
V2=$2
V3=$$
echo "$V1 $V2 $V3"
Calling:
./mypro.sh 1 2 3
Output:
1 2 7215
$$ is the pid of the bash process.
From the bash man page section Special Parameters:
$ Expands to the process ID of the shell. In a () subshell, it expands to the process ID of the current shell, not the subshell.

Resources