Bash/Awk: How can I run a command using bash or awk - bash

How can I run a command in bash, read the output it returns and check if there's the text "xyz" in there in order to decide if I run another command or not?
Is it easy?
Thanks

if COMMAND | grep -q xyz; then
#do something
fi
EDIT: Made it quiet.

For example:
command1 | grep "xyz" >/dev/null 2>&1 && command2
run command1
its output filer with grep
discard output from the grep
and if the grep was successful (so found the string)
execute the command2

You can pipe the output of the command to grep or grep -e.

Your specification is very loose, but here is an idea to try
output="$(cmd args ....)"
case "${output}" in
*targetText* ) otherCommand args ... ;;
*target2Text* ) other2Command ... ;;
esac
I hope this helps.

While you can accomplish this task many different ways, this is a perfect use for awk.
prv cmd | awk '/xyx/ {print "cmd" } ' | bash
this is what you want
for example,
i have a text file called temp.txt that only contains 'xyz'
If i run the following command I will exepct the output "found it"
$ cat temp.txt | awk '/xyz/ {print "echo found it"}' | bash
> found it
so what I am doing is piping the output of my previous command into awk, who is looking for the pattern xyz (/xyz/). awk will print the command, in this case echo found it, and pipe it to bash to execute them. simple one-liner doing what you asked. note you can customize the regex that awk looks for.

Related

How to use bash tail command inside a custom pipe command script?

I want to use tail in my custom pipe command.
For example, I want to execute this command:
>ls -1 | tail -n 1 | awk '{print "last file is "$1}'
>last file is test.txt
And I want to make it short by making my own custom script. It looks like this:
>ls -1 | myscript
>last file is test.txt
I know myscript can get input from "ls -1" by this code:
while read line; do
echo last file is $line
done
But I don't know how to use "tail -n 1" in the custom pipe command code above.
Is there a way to use a pipe command in another pipe command script?
Or do I have to implement the code which does the same process as "tail -n 1" myself?
I hope bash has some solution for this.
Try putting just this in myscript
tail -n 1 | awk '{print "last file is "$1}'
This works as the first command (tail) consumes the stdin of your script. In general, scripts work as though you typed their contest as-is to the terminal.

How to pipe an output of first command to go to specific location of next command?

I am running a command that spits out IPs, I need to feed it to another program at a specific location as it comes, how do I do it?
$ command1 | command2 -c configfile -i "$1" status
"$1" is where I want the result of command1 to go to.
Thanks.
xargs is your tool
$ command1 | xargs -I {} command2 -c configfile -i {} status
you can refer to the argument multiple times, for example
$ echo this | xargs -I {} echo {}, {}, and {}
this, this, and this
based on the last comment, perhaps you want to do something like this
$ var=$(command1) && command2 "$var" ... | command3 "$var" ...
To pass command2 a filename which will, when read, provide output from command1, the appropriate tool is process substitution:
command2 -c configfile -i <(command1) status
The <(...) syntax will be replaced with a filename -- on Linux, of the form /dev/fd/NN; on some other platforms a named pipe instead -- from which the output of command2 can be streamed.
There are probably 100 ways to do this in a bash shell. This one is quick and easy. We can use ifconfig, grep the IP address, use awk to pull it out and assign it to an environment variable. Then use the boolean operator to run the next command which uses the environment variable
IPADDR=`ifconfig | grep 172 | awk '{print $2}' | awk -F: '{print $2}'` && echo $IPADDR

Grep without filtering

How do I grep without actually filtering, or highlighting?
The goal is to find out if a certain text is in the output, without affecting the output. I could tee to a file and then inspect the file offline, but, if the output is large, that is a waste of time, because it processes the output only after the process is finished:
command | tee file
file=`mktemp`
if grep -q pattern "$file"; then
echo Pattern found.
fi
rm "$file"
I thought I could also use grep's before (-B) and after (-A) flags to achieve live processing, but that won't output anything if there are no matches.
# Won't even work - DON'T USE.
if command | grep -A 1000000 -B 1000000 pattern; then
echo Pattern found.
fi
Is there a better way to achieve this? Something like a "pretend you're grepping and set the exit code, but don't grep anything".
(Really, what I will be doing is to pipe stderr, since I'm looking for a certain error, so instead of command | ... I will use command 2> >(... >&2; result=${PIPESTATUS[*]}), which achieves the same, only it works on stderr.)
If all you want to do is set the exit code if a pattern is found, then this should do the trick:
awk -v rc=1 '/pattern/ { rc=0 } 1; END {exit rc}'
The -v rc=1 creates a variable inside the Awk program called rc (short for "return code") and initializes it to the value 1. The stanza /pattern/ { rc=0 } causes that variable to be set to 0 whenever a line is encountered that matches the regular expression pattern. The 1; is an always-true condition with no action attached, meaning the default action will be taken on every line; that default action is printing the line out, so this filter will copy its input to its output unchanged. Finally, the END {exit rc} runs when there is no more input left to process, and ensures that awk terminates with the value of the rc variable as its process exit status: 0 if a match was found, 1 otherwise.
The shell interprets exit code 0 as true and nonzero as false, so this command is suitable for use as the condition of a shell if or while statement, possibly at the end of a pipeline.
To allow output with search result you can use awk:
command | awk '/pattern/{print "Pattern found"} 1'
This will print "Pattern found" when pattern is matched in any line. (Line will be printed later)
If you want Line to print before then use:
command | awk '{print} /pattern/{print "Pattern found"}'
EDIT: To execute any command on match use:
command | awk '/pattern/{system("some_command")} 1'
EDIT 2: To take care of special characters in keyword use this:
command | awk -v search="abc*foo?bar" 'index($0, search) {system("some_command"); exit} 1'
Try this script. It will not modify anything of output of your-command and sed exit with 0 when pattern is found, 1 otherwise. I think its what you want from my understand of your question and comment.:
if your-command | sed -nr -e '/pattern/h;p' -e '${x;/^.+$/ q0;/^.+$/ !q1}'; then
echo Pattern found.
fi
Below is some test case:
ubuntu-user:~$ if echo patt | sed -nr -e '/pattern/h;p' -e '${x;/^.+$/ q0;/^.+$/ !q1}'; then echo Pattern found.; fi
patt
ubuntu-user:~$ if echo pattern | sed -nr -e '/pattern/h;p' -e '${x;/^.+$/ q0;/^.+$/ !q1}'; then echo Pattern found.; fi
pattern
Pattern found.
Note previous script fails to work when there is no ouput from your-command because then sed will not run sed expression and exit with 0 all the time.
I take it you want to print out each line of your output, but at the same time, track whether or not a particular pattern is found. Simply passing the output to sed or grep would affect the output. You need to do something like this:
pattern=0
command | while read line
do
echo "$line"
if grep -q "$pattern" <<< "$lines"
then
((pattern+=1))
fi
done
if [[ $pattern -gt 0 ]]
then
echo "Pattern was found $pattern times in the output"
else
echo "Didn't find the pattern at all"
fi
ADDENDUM
If the original command has both stdout and stderr output, which come in a specific order, with the two possibly interleaved, then will your solution ensure that the outputs are interleaved as they normally would?
Okay, I think I understand what you're talking about. You want both STDERR and STDOUT to be grepped for this pattern.
STDERR and STDOUT are two different things. They both appear on the terminal window because that's where you put them. The pipe (|) only takes STDOUT. STDERR is left alone. In the above, only the output of STDOUT would be used. If you want both STDOUT and STDERR, you have to redirect STDERR into STDOUT:
pattern=0
command 2>&1 | while read line
do
echo "$line"
if grep -q "$pattern" <<< "$lines"
then
((pattern+=1))
fi
done
if [[ $pattern -gt 0 ]]
then
echo "Pattern was found $pattern times in the output"
else
echo "Didn't find the pattern at all"
fi
Note the 2>&1. This says to take STDERR (which is File Descriptor 2) and redirect it into STDOUT (File Descriptor 1). Now, both will be piped into that while read loop.
The grep -q will prevent grep from printing out its output to STDOUT. It will print to STDERR, but that shouldn't be an issue in this case. Grep only prints out STDERR if it cannot open a file requested, or the pattern is missing.
You can do this:
echo "'search string' appeared $(command |& tee /dev/stderr | grep 'search string' | wc -l) times"
This will print the entire output of command followed by the line:
'search string' appeared xxx times
The trick is, that the tee command is not used to push a copy into a file, but to copy everything in stdout to stderr. The stderr stream is immediately displayed on the screen as it is not connected to the pipe, while the copy on stdout is gobbled up by the grep/wc combination.
Since error messages are usually emitted to stderr, and you said that you want to grep for error messages, the |& operator is used for the first pipe to combine the stderr of command into its stdout, and push both into the tee command.

bash, execute "edmEventSize" command but it is not found when i tyoe bash script.sh

i hava a file in which using the command "edmEventSize" i can
extract a piece of information of that file (it is a number)
but know i have 700 files on which i have to execute that command
and i am trying to do it on a bash script but i cannot event do it for just
one file since i get "edmEventSize command not found", i already look for
more information but since i am new at bash i can not solve this task
Thank you in advanced
this is my script
#/usr/bin/env sh
for i in {1..700};
do
FILE="Py6_BstoJpsiKs0_7TeV_RECO_Run-0${i}.root"
edmEventSize... $FILE.root > salida${i}.log
done
head *.log | grep "^File" | cut -f4 > a.txt
rm *.log
As everyone would suggest, you can simplify your script like this:
#/bin/bash
for i in {1..700}; do
FILE="Py6_BstoJpsiKs0_7TeV_RECO_Run-0${i}.root"
/path/to/EdmEventSize "$FILE.root"
done | awk -F $'\t' '/^File/{print $4}' > a.txt
If your files actually are in the format of Py6_BstoJpsiKs0_7TeV_RECO_Run-####.root maybe the command you really need is:
printf -v FILE 'Py6_BstoJpsiKs0_7TeV_RECO_Run-%04d.root' "$i"

basename or endswith in awk?

I'm using awk to get the pid of a specific process by name.
#!/bin/sh
for pid in $(ps | awk '$4 == "foo" { print $1 }')
do
i=1
echo "killing foo at pid $pid"
kill $pid && echo 'ok' || echo 'failed'
done
where ps output on OSX is something like:
$ ps
PID TTY TIME CMD
1858 ttys000 0:00.15 -bash
4148 ttys000 0:01.37 /a/b/c/foo
but my shell script only works if the process in the CMD column is exactly foo (no absolute paths).
I'm thinking I can use basename, but how do I call it from awk?
OR
Is there something like $4.endswith('foo') in awk?
awk can use regular expressions. So rather than $4 == "foo", you could do something like:
awk '$4 ~ /\/foo$/ { print $1 }'
The regular expression is between the / and /. \/foo$ says "a backslash followed by foo and then the end of the field." In this case, your field is /a/b/c/foo, so it would match.
In addition to regular expressions, you can use string manipulation as substr($4, length($4) - 2) == "foo".
First of all, you should use a while read construct instead of for. With a for loop, you have to wait for the command in the $(...) to execute before the for loop can start. Plus, you could overrun the command buffer. Not a major issue today, but when you run into it, you'll never get any indication that something went wrong:
ps | awk '$4 == "foo" { print $1 }' | while read pid
do
i=1
echo "killing foo at pid $pid"
kill $pid && echo 'ok' || echo 'failed'
done
Nothing much to do with your problem, but I need to get that off my chest.
Now on to your Answer:
The awk automatically does a loop, so since you're using awk anyway, why not let it do the work for you? Most awk implementations have a system command, so you can execute the kill right inside your awk script.
Also, take a look at man ps and check your PS options. Most ps command take the -o option which allows you to specify which fields to print. My ps command (which just happens to be on OS X) allows you to use ucomm which is just the name of the command without the directory or the command line parameters. Sounds useful for your situation. I'll use ps -opid, ucomm which will print only two columns for the ps command: The PID, and the command name sans directory and command line parameters:
$ ps -o pid,ucomm
PID UCOMM
1 launchd
10 kextd
11 UserEventAgent
12 mDNSResponder
13 opendirectoryd
14 notifyd
15 fseventsd
16 configd
17 diskarbitrationd
18 syslogd
(Just one warning, it looks like ucomm cuts things off at the 14th or 16th column. Just to let you know).
For awk, we can use the -v parameter to define an Awk variable. This is useful in case you write a shell script, and want awk to take the name of the program from the command parameters.
In Awk, as you know, $1 will represent the PID and $2 will represent the command sans the directory or command line parameters.
We can use the $2 == command to filter out all the lines in your ps command where the command is foo. This is a shortcut to the if statement.
And, most implementations of awk have a system function which can be used to run commands like kill from inside your awk script. Looks like we have almost everything we need:
ps -o pid,ucomm | awk -v command="foo" '$2 == command {system("kill " $1)}'
That's a nice clean one liner, but it doesn't check the status of the system command, nor does it echo out when it fails or what command and PID you're killing.
Good thing that Awk isn't just an abstruse command. It's also an abstruse programming language. We could test the return of the system command and add a few print statements. I didn't test this, but it should be pretty close:
ps -o pid,ucomm | awk -v command="foo" '$2 == command {
print "Killing " $2 " at PID " $1
if (system("kill " $1)) {
print "Failed to kill PID " $1
}
}'
The if may have to be if (! system("kill " $1)) { instead.
For the condition, you can use
$4 ~ /\/foo$/
which translates to "the 4th field matches an (escaped) forward-slash followed by 'foo' at the end of the line."
It seems you are trying to kill all processes which has name 'foo', how about:
killall foo
When looking for pids, you must exclude the awk process, otherwise it could kill the search process instead.
kill $(ps -aef | awk -v program="mplayer" '$0 ~ program {if ($0 !~ "awk") print $2}')

Resources