BASH script help using TOP, GREP and CUT - bash

Use Top command which repeats 5 times, pipe the results to Grep and Cut command to print the PID for init process on your screen.
Hi all, I have my line of code:
top -n 5 | grep "init" | cut -d" " -f3 > topdata
But I cannot see any output to verify that it's working.
Also, the next script asks me to use a one line command which shows the total memory used in megabytes. I'm supposed to pipe results from Free to Grep to select or filter the lines with the pattern "Total:" then pipe that result to Cut and display the number representing total memory used. So far:
free -m -t | grep "total:" | cut -c25-30
Also not getting any print return on that one. Any help appreciated.

expanding on my comments:
grep is case sensitive. free says "Total", you grep "total". So no match! Either grep for "Total" or use grep -i.
Instead of cut, I prefer awk when I need to get a number out of a line. You do not know what length the number will be, but you know it will be the first number after Total:. So:
free -m -t | grep "Total:" | awk '{print $2}'
For your top command, if you have no init process (which you should, but it would probably not show in top), just grep for something else to see if your code works. I used cinnamon (running Mint). The top command is:
top -n 5 | grep "cinnamon" | awk '{print $1}'
Replace "cinnamon" by "init" for your requirement. Why $1 in the awk? My top puts the PID in the first column. Adjust accordingly.
Overall, using cut is good when you have a string that is delimited by some character. Ex. aaa;bbb;ccc, you would cut on -d';'. But here the numbers might have different lengths so using cut is not (IMHO) the best solution.

The init process has PID 1, to there's no reason to do like this.
To find the PID of a process in general, I'd recommend:
pidof <name>

Related

Using grep to search for a whole word/phrase with only part

I am trying to fetch the current "Device Node" (for example disk0s4) for my Bootcamp partition on my MacBook Pro using the terminal command $ diskutil info "BOOTCAMP" | grep "Node". This works nicely. I want to create a program that does a few things to the partition/volume and because it could potentially change nodes (because I screw with my filesystem).
Thus I need to create a variable or store the value of the device node (and only the device node) so I can use it later. Here is what I have tried.
$ grepVar=$(diskutil info "BOOTCAMP" | grep "Node")
$ A="$(cut -d'/' -f1 <<<"$grepVar")"
$ echo "$A hi"
Device Node: hi
The actual file reads as this when I run diskutil info "BOOTCAMP" | grep "Node":
Device Node: /dev/disk0s4
Obviously the cut cut out the wrong part, and kept the spacing. I experimented with a few other techniques but with no prevail. What would anyone recommend? The -o method just gave me part of the /dev/disk0s4, not the entire phrase/word. Would I just have to phrase it after "grepping" it? How?
Thanks a ton
Remember that grep is an acronym. Global Regular Expression Print -- like the command you might use in vi to list lines that match a regex. Extra functionality like PCRE is not portable and may not be available in all systems -- or even in yours without software being installed.
Often sed provides the quickest or most succinct way to manipulate a stream of text. For example:
diskutil info "BOOTCAMP" | sed -ne '/Device Node:/s#.*/##p'
This finds the line of text you're looking at, strips off everything up to the last slash, and prints the remainder of the line. Putting this in a variable is as simple as:
node=$(diskutil info "BOOTCAMP" | sed -ne '/Device Node:/s#.*/##p')
or
read -r node < <(diskutil info "BOOTCAMP" | sed -ne '/Device Node:/s#.*/##p')
You could alternately process the output in awk like so:
diskutil info "BOOTCAMP" | awk -F/ '/Device Node:/ { print $NF }'
This, again, finds the line you're interested in, and prints the last field .. with field separators set to the forward slash.
If you REALLY wanted to use only grep for this, you could do some pipe-fitting:
diskutil info "BOOTCAMP" | grep 'Device Node:' | grep -o '/[^/]*$' | grep -Eo '[^/]+'
And of course, the final grep could be replaced with cut -d/ -f2 if you wish.
Naturally, all of these fail if BOOTCAMP doesn't exist.
In macOS you could use pcregrep, for example, to only obtain disk0s4:
echo "Device Node: /dev/disk0s4" | pcregrep -o "/dev/\K.*"
It will return:
disk0s4
The option -o is for showing only the part of the line that matched a pattern instead of the whole line.
The \K can be read as excluding everything to the left before it and return only the right part.

What is the proper method to pipe the output of the cut command into a grep command?

I am currently learning a little more about using Bash shell on OSX terminal. I am trying to pipe the output of a cut command into a grep command, but the grep command is not giving any output even though I know there are matches. I am using the following command:
cut -d'|' -f2 <filename.txt> > <temp.txt> | grep -Ff <temp.txt> <searchfile.txt> > <filematches.txt>
I was thinking that this should work, but most of the examples I have seen normally pipe grep output into the cut. My goal was to cut field 2 from the file and use that as the pattern to search for in . However, using the command produced no output.
When I generated the temp.txt first with the cut command and then ran the grep on it manually with no pipe, the grep seemed to run fine. I am not sure why this is?
You can use process substitution here:
grep -Ff <(cut -d'|' -f2 filename.txt) searchfile.txt > filematches.txt
<(cut -d'|' -f2 filename.txt) is feeding cut command's output to grep as a file.
Okay, a reason this line doesn't behave as you expect
cut -d'|' -f2 <filename.txt> > <temp.txt> | grep -Ff <temp.txt> <searchfile.txt> > <filematches.txt>
is that the output of your cut is going to temp.txt. You're not sending anything to the pipe. Now, conveniently pipe also starts a new commend, so it doesn't matter much -- grep runs and reads searchfile.txt.
But what are you trying to do? Here's what your command line is trying to do:
take the second pipe-delimited field from filename.txt
write it to a file
run grep ...
... using the contents of the file from 2 as a grep search string (which isn't going to do what you think either, as you're effectively asking grep to look for the pattern match1\nmatch2...)
You'd be closer with
cut ... && grep ...
as that runs grep assuming cut completes effectively. Or you could use
grep -f `cut ...`
which would put the results on the command line. You need to mess with quoting, but you're still going to be looking for a line containing ALL of your match fields from cut.
I'd recommend maybe you mean something like this:
for match in `cut ...`
do
grep -f $match >> filematches.txt
done

Using bash ps and cut together

I need to extract PID, UID and command fields from 'ps' and I have tried it like this:
ps -L u n | cut -f 1,2,13
For some reason, this behaves as there is no cut command whatsoever. It just returns normal ps output. Then, I tried
ps -L u n | tr -s " " | cut -d " " -f 1,2,13 and this returns total nonsense. Then, I tried playing with it and with this:
ps -L u n | tr -s " " | cut -d " " -f 2,3,14
and this somehow returns what I need (almost, and I don't understand why that almost works), except that it cuts out the command field in the middle of it. How can I get what I need?
ps is printing out space separators, but cut without -d uses the tab character. The tr -s squeezes the spaces together to get more of the separation that you want, but remember that there is the initial set of spaces (squeezed to one) hence why you need to add 1 to each field. Also, there are spaces in the commands for each word. This should work:
ps -L u n | tr -s " " | cut -d " " -f 2,3,14-
Is there any particular reason for using cut?
I guess this will do what you want:
ps -eopid,uid,cmd
You can use awk to clean up your command, like so:
ps -L u n | awk '{ print $1,$2,$13 }'
The question is what to do once you have a list. I find cut kludgy, so instead of cut I pass the list to a while read loop.
"While read" recognizes non-blank values on a line, so in this example, "a" is the first value, "b" is the second and "c" is the rest of the line. I am only interested in the first 2 values, process owner and process ID; and I basically abuse the case statement rather than use an "if". (Even though grep filtered, I don't want to kill processes where the owner name might be embedded elsewhere in the line)
ps -ef | grep owner | grep -v grep | while read a b c;
do
case $a in
"owner")
kill -9 $b
;;
esac;
done

Bash Output different from command line

I have tried all kinds of filters using grep to try and solve this but just cannot crack it.
cpumem="$(ps aux | grep -v 'grep' | grep 'firefox-bin' | awk '{printf $3 "\t" $4}'
I am extracting the CPU and Memory usage for a process and when I run it from the command line, I get the 2 fields outputted correctly:
ps aux | grep -v 'grep' | grep 'firefox-bin' | awk '{printf $3 "\t" $4}'
> 1.1 4.4
but the same command executed from within the bash script produces this:
cpumem="$(ps aux | grep -v 'grep' | grep 'firefox-bin' | awk '{printf $3 "\t" $4}')"
echo -e cpumem
> 1.1 4.40.0 0.10.0 0.0
I am guessing that it is picking up 3 records, but I just don't know where from.
I am filtering out any other grep processes by using grep -v 'grep', can someone offer any suggestions or a more reliable way ??
Maybe you have 3 records because 3 firefox are running (or one is running, and it is threading itself).
You can avoid the grep hazzle by giving ps and option to select the processes. E.g. the -C to select processes by name. With ps -C firefox-bin you get only the firefox processes. But this does not help at all, when there is more than one process.
(You can also use the ps option to output only the columns you want, so your line would be like
ps -C less --no-headers -o %cpu,%mem
).
For the triple-record you must come up with a solution, what should happen, where more than one is running. In a multiuser environment with programms that are threading there can always be situations where you have more than one process of a kind. There are many possible solution where none can help you, as you dont say, way you are going to do with it. One can think of solutions like selecting only from one user, and only the one with the lowest pid, or the process-leader in case of groups, to change the enclosing bash-script to use a loop to handle the multiple values or make it working somehow different when ps returns multiple results.
I was not able to reproduce the problem, but to help you debug, try print $11 in your awk command, that will tell you what process it is talking about
cpumem="$(ps aux | grep -v 'grep' | grep 'firefox-bin' | awk '{printf $3 "\t" $4 "\t" $11 "\n"}')"
echo -e cpumem
It's actually an easy fix for the output display; In your echo statement, wrap the variable in double-quotes:
echo -e "$cpumem"
Without using double-quotes, newlines are not preserved by converting them to single-spaces (or empty values). With quotes, the original text of the variable is preserved when outputted.
If your output contains multiple processes (i.e. - multiple lines), that means your grep actually matched multiple lines. There's a chance a child-process is running for firefox-bin, maybe a plugin/container? With ps aux, the 11th column will tell you what the actual process is, so you can update your awk to be the following (for debugging):
awk '{printf $3 "\t" $4 "\t" $11}'

How do you pipe input through grep to another utility?

I am using 'tail -f' to follow a log file as it's updated; next I pipe the output of that to grep to show only the lines containing a search term ("org.springframework" in this case); finally I'd like to make is piping the output from grep to a third command, 'cut':
tail -f logfile | grep org.springframework | cut -c 25-
The cut command would remove the first 25 characters of each line for me if it could get the input from grep! (It works as expected if I eliminate 'grep' from the chain.)
I'm using cygwin with bash.
Actual results: When I add the second pipe to connect to the 'cut' command, the result is that it hangs, as if it's waiting for input (in case you were wondering).
Assuming GNU grep, add --line-buffered to your command line, eg.
tail -f logfile | grep --line-buffered org.springframework | cut -c 25-
Edit:
I see grep buffering isn't the only problem here, as cut doesn't allow linewise buffering.
you might want to try replacing it with something you can control, such as sed:
tail -f logfile | sed -u -n -e '/org\.springframework/ s/\(.\{0,25\}\).*$/\1/p'
or awk
tail -f logfile | awk '/org\.springframework/ {print substr($0, 0, 25);fflush("")}'
On my system, about 8K was buffered before I got any output. This sequence worked to follow the file immediately:
tail -f logfile | while read line ; do echo "$line"| grep 'org.springframework'|cut -c 25- ; done
What you have should work fine -- that's the whole idea of pipelines. The only problem I see is that, in the version of cut I have (GNU coreutiles 6.10), you should use the syntax cut -c 25- (i.e. use a minus sign instead of a plus sign) to remove the first 24 characters.
You're also searching for different patterns in your two examples, in case that's relevant.

Resources