How does this sed command works? - shell

I came across the following sed command which I found here https://github.com/shama/grunt-hub:
ps -ef | sed -n '/grunt/{/grep/!p;}'
Could someone explain me how does the sed part work? What's the purpose of {/grep/!p;}?
Thanks for the attention!

compare the output of following two commands:
ps -ef | sed -n '/grunt/p' and ps -ef | sed -n '/grunt/{/grep/!p;}'.
You will notice later is not printing one additional like which contains process id of the grep command you hit. This would be equivalent to:
ps -ef |grep grunt |grep -v grep
Its like print all the lines containing grunt but not the line also containing grep in it

Related

egrep gives me what I want, grep -E does not

I have read that egrep is deprecated in favour of grep -E so I'm looking back at my code and changing it where I find it.
But I see that the results are not always as expected.
With egrep, I get my list of running Oracle instances:
ps -ef|grep [p]mon|awk -F'ora_pmon_' '{print $NF}'|egrep -v 'ASM|^$'
halana
bila
halsasa
loana
However with grep -E, I get part of the awk in the results:
ps -ef|grep [p]mon|awk -F'ora_pmon_' '{print $NF}'|grep -Ev 'ASM|^$'
halana
bila
halsasa
{print $NF}
loana
Excluding 'print' obviously works but is it the right way to go about it?:
ps -ef|grep [p]mon|awk -F'ora_pmon_' '{print $NF}'|grep -Ev 'ASM\|^$|print'
halana
bila
halsasa
loana
Is this effect due to the fact that grep -E allows additional regular expression syntax?
Suggesting to simplify your line, eliminate all grep commands:
pgrep -af 'pmon'|awk -F'ora_pmon_' '!/ASM|^$/{print $NF}'
Fold first grep command into pgrep command.
Fold second grep command into awk scirpt !/ASM|^$/{print $NF}
About grep -E vs egrep
There is no difference whatsoever between these commands (they're even provided by the same executable on most operating systems); your old code was vulnerable to this bug too, and if you didn't see it, that's just a matter of being "lucky" in ps getting enough of the process list read before the shell gets to setting up the awk part of the pipeline.
Solving Your Problem
The right answer is already given by #DudiBoy, but the answer with the smallest possible change would be to make your awk (assuming it has GNU extensions and accepts regexes in the field separator specification) escape the string pmon the same way your first grep command does:
ps -ef|grep '[p]mon'|awk -F'ora_[p]mon_' '{print $NF}'|grep -Ev 'ASM|^$'

How to combine output from awk and pipe into htop?

$ ps -ef | grep python | awk -F' ' '{print $2}'
9825
4470
4619
$ htop -p 9825,4470,4619
For now, I have to make two separate commands in order to watch all python processes within htop. Is there a way that I can pipe all the results from awk and feed them into htop?
If you have pgrep (you probably do):
htop -p $(pgrep python | paste -sd,)
You could avoid grep and use only awk using something like:
ps -ef | awk '/[p]ython/{print $2}'
Then you could use:
htop -p $(ps -ef | awk -v ORS=, '/[p]ython/{print $2}')
Notice the [] around the p, this is a nice trick to avoid printing the second command itself:
ps -ef | awk '/[p]ython/{print $2}'
| |
cmd 1 cmd 2
it works because awk will translate the regex [p] to say something like "match characters from [p] in this case, p only, followed by ython:
[p]ython != python

Redirecting piped command into a file in bash

I'm trying to do the following:
ping some.server.com | grep -Po '(?<=\=)[0-9].\.[0-9]' >> file.dat
i.e. I run a command (ping), grep part of the output and redirect the result of grep into a file to be inspected later. While the command itself works (i.e. the part before '>>'), nothing gets written into the file.
How do I do this correctly?
Use --line-buffered argument.
ping some.server.com | grep --line-buffered -Po '(?<=\=)[0-9].\.[0-9]' >> file.dat

Redirect the output to two processes and merge

Background: sometimes I use ps aux | grep process_name to figure out some statistics of a process. But I can't remember the headers of ps output. So I need to do:
ps aux | tee 1.txt | grep process_name > 2.txt
cat 1.txt | head -1 | cat - 2.txt
So my questions is: is there a way to achieve this without the two temporary files, and preferably use one line of commands instead of two lines?
The simplest way is just to use something a bit smarter than grep. For example:
ps aux | perl -ne 'print if $. == 1 || m/process_name/'
will print the first line of ps aux's output, plus any line that matches process_name (which is a Perl regular expression — more powerful than POSIX BRE's, and therefore more complicated, but I don't think you'll find any surprises if you're just searching for a process name).
Edited to add: For that matter, since ps aux's headers are unlikely to change between runs, you could write:
ps aux | head -1 ; ps aux | grep process_name
though I don't know if that still counts as "one line of commands". :-)
More options: Since you don't like the above, here are some more. This one will read and print the first line directly, and then leave the rest for grep to read and process:
ps aux | ( ( read -r LINE ; echo "$LINE" ) ; grep process_name )
This one will cause every line to be written both to standard output (file descriptor 1) and to a custom file descriptor (3); then head gets standard output, while grep gets what was written to the custom file descriptor:
( ( ps aux | while read -r LINE ; do echo "$LINE" ; echo "$LINE" 1>&3 ; done | head -1 1>&4 ) 3>&1 | grep process_name ) 4>&1
That one is the most general way that I know of to do what you're asking for — at least, using only Bash builtins and features — but as you can see, it's quite unwieldy, which is why I recommend the simpler ways when you don't need the full power of this approach. Also, that last one no longer guarantees that headers come first; on my system, the output of head seems to consistently end up after the output of grep. (I suppose that could be addressed by swapping the positions of the head and grep commands, but that still wouldn't be reliable so far as I know, and it seemed to have weird effects when I tried it just now.)
Or, since I never remember perl or awk without looking it up:
ps aux | head -n 1 && ps aux |grep process_name
Another dirty trick is to memorize that the header starts with 'USER' and do
ps aux | grep -E 'USER | process_name'
This will also show the grep command itself :)
Or
ps aux | egrep "(^USER +PID)|^process_name"
sed will do exactly what you want:
ps aux | sed -n -e '1 p; /PROCESS_NAME/p'
The sed command line:
-n print only the specified lines
-e execute the following script
1 p print the first line
; command separator
/PROCESS_NAME/p print lines containing PROCESS_NAME
use the name(s) of processes with -C
ps u -C java -C totem
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
stefan 16878 0.1 10.5 429036 107868 pts/7 Sl+ 12:51 0:24 /opt/java/bin/java -Xmx256M -Xms32M -Xbootclasspa
stefan 25791 0.5 3.8 202800 38984 ? Sl 17:45 0:16 totem file:///home/stefan/Desktop/scala/dritte/dl
I hope this doesn't only work on Linux. :)
I'm always baffled to see, how many people don't know (or use) the options of ps - maybe there are too many? It's always ps | grep x. :)

Bash grep sth. then to find the position

I've long been wondering about this question;
say I first try to grep some lines from a file:
cat 101127_2.bam |grep 'TGATTACTTGCTTTATTTTAGTGTTTAATTTGTTCTTTTCTAATAA'
Then it'll pop out the whole line containing this string.
However, can we use some simple bash code to locate at which line this string locates? (100th? 1000th?...)
grep -n 'TGATTACTTGCTTTATTTTAGTGTTTAATTTGTTCTTTTCTAATAA' 101127_2.bam
I found it using man grep and writing /line number
// EDIT: Thanks #Keith Thompson I'm editing post from cat file | grep -n pattern to grep -n pattern file, I was in a hurry sorry
try this:
cat 101127_2.bam |grep -n 'TGATTACTTGCTTTATTTTAGTGTTTAATTTGTTCTTTTCTAATAA'
This might work for you too:
sed '/TGATTACTTGCTTTATTTTAGTGTTTAATTTGTTCTTTTCTAATAA/=;d' 101127_2.bam
or
sed -n '/TGATTACTTGCTTTATTTTAGTGTTTAATTTGTTCTTTTCTAATAA/=' 101127_2.bam
The above solutions only output the matching line numbers, to see the lines matched too:
sed '/TGATTACTTGCTTTATTTTAGTGTTTAATTTGTTCTTTTCTAATAA/!d;=' 101127_2.bam
or
sed -n '/TGATTACTTGCTTTATTTTAGTGTTTAATTTGTTCTTTTCTAATAA/{=;p}' 101127_2.bam

Resources