LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Server (https://www.linuxquestions.org/questions/linux-server-73/)
-   -   grep upto specific keyword from a file under Redhat 6 (https://www.linuxquestions.org/questions/linux-server-73/grep-upto-specific-keyword-from-a-file-under-redhat-6-a-4175578435/)

Tridibesh 04-27-2016 01:25 AM

grep upto specific keyword from a file under Redhat 6
 
I am trying to write a script for checking the SAN path status in Linux nodes under the multipath. For checking the multipath status, I am sorting out the mpath names and looping the multipath -ll output with the mpath name to process the output. By this I'm able to check the status of all the LUNs. But problem is if the server has many LUNs, it is taking long time to execute the script because of looping the multipath -ll command multiple times. So I was thinking if I can redirect the multipath -ll output to a file and then process the mpath output instead of looping the multipath -ll output it will speed up the execution time. I can grep out information for the specific mpath device from that file with the -A option in grep command if the no of following lines are same for all the LUNs. But here problem is some mpath may have 2 underline devices or 4 or 8 devices. So following lines count will be different for each mpath device. So only if I can grep until the next occurrence of mpath keyword, I can accomplish my purpose. So if anyone can help me with how to do (may be via grep or sed or awk or any other method) so I will be very grateful to him.

example output of multipath -ll command output:-

mpathj (360a980003246694b552b425830593539) dm-19 NETAPP,LUN
size=12M features='3 queue_if_no_path pg_init_retries 50' hwhandler='1 alua' wp=rw
|-+- policy='round-robin 0' prio=50 status=active
| |- 1:0:3:0 sdu 65:64 active ready running
| `- 0:0:2:0 sdy 65:128 active ready running
`-+- policy='round-robin 0' prio=10 status=enabled
|- 1:0:1:0 sds 65:32 active ready running
`- 0:0:1:0 sdw 65:96 active ready running
mpathi (360002ac0000000000000033300012147) dm-3 3PARdata,VV
size=2.0G features='1 queue_if_no_path' hwhandler='0' wp=rw
`-+- policy='round-robin 0' prio=1 status=active
|- 0:0:0:10 sdi 8:128 active ready running
`- 1:0:0:10 sdr 65:16 active ready running


So from the above output of multipath -ll command how can I grep or print only the info about a specific mpath device e.g mpathi (you can see the no of lines is different for each mpath device).

Turbocapitalist 04-27-2016 03:45 AM

grep won't fit there as you point out because of the variable number of lines.

With awk it is easy and can be done in three steps.

Code:

awk '/^mpathj/ && !flag  { print; flag++; next }; /^mpath/ && flag { exit }; flag { print } ;
That represents three if-then conditions in serial, separated by semicolons. 'print' is short for 'print $0' There may be a sounder or more elegant way to do it.

That will work for either the pattern "mpathj" or "mpathi" or similar. You'll have to adapt it yourself to fit it into your actual workflow.

Tridibesh 04-27-2016 05:16 AM

Thanks for the response. I am not that much good with awk command usage. Can you please explain the syntax you provided?

Turbocapitalist 04-27-2016 05:27 AM

flag is a variable. undefined it starts out as equivalent to 0. Using ++ increments it, setting it to 1.

/^mpathj/ looks for "mpathj" at the start of the input line. Actually it's the caret ^ that indicates the beginning of the line. Without it, the pattern would match "mpathj" anywhere on the line.

awk statements are separated by semicolons. By default they are if-then clauses, with the action being contained in the braces. So in pseudo-code what you are seeing above is this:

Code:


while ( $0 = readnewline ) {
        if ( $0 =~ /^mpathj/ ) then
                {
                        print $0;  # print the current line
                        $flag++;    # increment the flag
                        next;      # skip the rest of the stuff below and start again and read the next line
                }
        if ( $0 =~ /^mpath/ AND $flag != 0 ) then
                {
                        exit;  # quit the awk program now
                }
        if ( $flag != 0 ) then
                {
                        print $0;  # print the current line
                }
}

"print" by itself is short for "print $0", which means the whole line

Try taking a look at the manual page for awk now, some parts should look familiar.

Tridibesh 04-27-2016 05:38 AM

Thanks Turbocapitalist. I will have a look in the man page and will try to understand the code. If I have any doubt I will come back and will try to clear that.

HMW 04-27-2016 06:32 AM

You can also do this with sed:
Code:

sed -n '/mpathj/,/mpath[a-z]/p' mpath.txt | sed '$ d'
A bit cleaner imho, but then again I'm a novice (at best!) at awk.

Given this infile:
Code:

mpathj (360a980003246694b552b425830593539) dm-19 NETAPP,LUN
size=12M features='3 queue_if_no_path pg_init_retries 50' hwhandler='1 alua' wp=rw
|-+- policy='round-robin 0' prio=50 status=active
| |- 1:0:3:0 sdu 65:64 active ready running
| `- 0:0:2:0 sdy 65:128 active ready running
`-+- policy='round-robin 0' prio=10 status=enabled
|- 1:0:1:0 sds 65:32 active ready running
`- 0:0:1:0 sdw 65:96 active ready running
mpathi (360002ac0000000000000033300012147) dm-3 3PARdata,VV
size=2.0G features='1 queue_if_no_path' hwhandler='0' wp=rw
`-+- policy='round-robin 0' prio=1 status=active
|- 0:0:0:10 sdi 8:128 active ready running
`- 1:0:0:10 sdr 65:16 active ready running

I get this result:
Code:

sed -n '/mpathj/,/mpath[a-z]/p' mpath.txt | sed '$ d'
mpathj (360a980003246694b552b425830593539) dm-19 NETAPP,LUN
size=12M features='3 queue_if_no_path pg_init_retries 50' hwhandler='1 alua' wp=rw
|-+- policy='round-robin 0' prio=50 status=active
| |- 1:0:3:0 sdu 65:64 active ready running
| `- 0:0:2:0 sdy 65:128 active ready running
`-+- policy='round-robin 0' prio=10 status=enabled
|- 1:0:1:0 sds 65:32 active ready running
`- 0:0:1:0 sdw 65:96 active ready running

In other words, only from 'mpathj' up until the line before 'mpathi'.

Best regards,
HMW

syg00 04-27-2016 07:24 AM

Perhaps a slight mod to handle the situation where multiple of the mpathj stanza exist
Code:

sed -n '/mpathj/,/mpath[a-z]/{/mpath[^j]/d ; p}' mpath.txt

Tridibesh 04-28-2016 01:21 AM

Thanks syg00 & HMW for your kind response.
I was trying and the code worked. Now what I am trying, is creating for loop with the mpathX putting them into a variable and trying to use the variable in the code provided by you. But some how the sed command is not reading the variable properly and showing nothing as output. So can you please help how to use the mpath as variable in the sed command provided by you?

Code:

00:14:40 # sed -n '/mpathi/,/mpath[a-z]/{/mpath[^i]/d ; p}'  /tmp/mpath.txt
mpathi (360002ac0000000000000033300012147) dm-3 3PARdata,VV
size=2.0G features='1 queue_if_no_path' hwhandler='0' wp=rw
`-+- policy='round-robin 0' prio=1 status=active
  |- 0:0:0:10 sdi 8:128  active ready running
  `- 1:0:0:10 sdr 65:16  active ready running

root []@test1:(SL=1):(RC=0):/root
00:14:58 # cat /tmp/mpath.txt | grep ^mpath | awk '{print $1}'
mpathe
mpathd
mpathc
mpathb
mpatha
mpathk
mpathj
mpathi
mpathh
mpathg
mpathf

root []@test1:(SL=1):(RC=0):/root
00:16:13 # cat /tmp/mpath.txt | grep ^mpath | awk '{print $1}'>/tmp/mpath_list

root []@test1:(SL=1):(RC=0):/root
00:16:23 # for i in `cat /tmp/mpath_list`
> do
> sed -n '/"$i"/,/mpath[a-z]/{/"$i"/d ; p}' /tmp/mpath.txt
> done

root []@test1:(SL=1):(RC=0):/root


syg00 04-28-2016 01:55 AM

Use double-quotes around the sed code rather than single. You don't need the internal double-quotes you added.
I don't think that will do what you want, but I'll leave you to debug your changes.

Turbocapitalist 04-28-2016 02:23 AM

Also, a simpler way to do something like this,

Code:

cat /tmp/mpath.txt | grep ^mpath | awk '{print $1}'>/tmp/mpath_list
would be like this:

Code:

awk '/^mpath/ { print $1 }' /tmp/mpath.txt > /tmp/mpath.list
That eliminates cat, which is often not needed, as well as a redundant grep. The awk language includes some pattern matching like grep.

About about the quotes, the double and single quotes work differently. The single quotes preserve the literal value of what's between them. The double quotes allow some processing. Compare:

Code:

echo "Home is $HOME for $USER."
echo "Today is $(date +%F)."

echo 'Home is $HOME for $USER.'
echo 'Today is $(date +%F).'



All times are GMT -5. The time now is 09:27 PM.