1

Here is a sample text.(It's name is 20210622_090009)

nvmeSerial      Endpoint        nvmeSpeed           nvmeWidth
================================================================================
nvme0n1         c7:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme1n1         c8:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme2n1         c9:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme3n1         ca:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme4n1         85:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme5n1         86:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme6n1         87:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme7n1         88:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme8n1         41:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme9n1         42:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme10n1        43:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme11n1        44:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme12n1        45:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme13n1        46:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme14n1        47:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme15n1        48:00.0                             Width x2 (downgraded)
nvme16n1        01:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme17n1        02:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme18n1        03:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme19n1        04:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme20n1        05:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme21n1        06:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme22n1        07:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme23n1        08:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme24n1        09:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme25n1        0a:00.0     Speed 32GT/s (ok)       Width x2 (downgraded)

Here is the script:

#! /bin/bash
IFS_old="$IFS"
IFS=$'\n'

for line in $(cat 20210622_090009.txt | tail -n 26 | cut -f 5 | awk '{print $2}' ) do echo "$line" done IFS="$IFS_old" exit 0

The script output is

8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
32GT/s

I want to grab the nvmeSpeed(Ex:8GT/s) whether the speed has the number or not.

As you see, nvmeSpeed in nvme15n1 is a whitespace.

And the output doesn't show up.

My question is:

How to awk a whitespace to become a for loop input ?

Jacky
  • 161

4 Answers4

11

awk alone can do all of this. You don't need a shell script wrapper, you certainly don't need anything as baroque as cat 20210622_090009.txt | tail -n 26 | cut -f 5 | awk '{print $2}'), and you should avoid using a shell while-read loop (or a for loop over the output of a language like awk or perl) wherever possible (see Why is using a shell loop to process text considered bad practice? for reasons why).

Rule of thumb: if you ever find yourself thinking "I want to iterate over awk's output" you should change your thinking to "I should almost certainly do this with just awk", or a shell wrapper that sets up input and output redirection for awk to do the bulk processing work. Same for perl and most other languages. Any other language will do the processing work better than shell, and you're only going to make your job harder by trying to do it with shell.

Anyway, the following script prints column 4 if there are exactly 8 columns (NF == 8). If there are less than 8 columns (NF < 8), it prints a blank line. In both cases, it ignores the two headers lines at the beginning of every input file (it can handle one or more filename arguments. FNR < 3 {next}. In awk, NR is the total number of lines read while FNR is the line number of the current file).

$ awk 'FNR < 3 {next}; NF == 8 {print $4}; NF < 8 {print ""}' 20210622_090009.txt  
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s

8GT/s 8GT/s 8GT/s 8GT/s 8GT/s 8GT/s 8GT/s 8GT/s 8GT/s 32GT/s

dhag
  • 15,736
  • 4
  • 55
  • 65
cas
  • 78,579
7

Looks like you have fixed width fields so, using GNU awk for FIELDWIDTHS and gensub():

$ awk -v FIELDWIDTHS='16 12 24 *' '
    NR>2 {
        gsub(/^ *| *$/,"",$3)
        print gensub(/.* ([^ ]+) .*/,"\\1",1,$3)
    }
' file
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s
8GT/s

8GT/s 8GT/s 8GT/s 8GT/s 8GT/s 8GT/s 8GT/s 8GT/s 8GT/s 32GT/s

The above first identifies the contents of each field by the width of each field:

$ cat file
nvmeSerial      Endpoint        nvmeSpeed           nvmeWidth
================================================================================
nvme0n1         c7:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme1n1         c8:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
nvme15n1        48:00.0                             Width x2 (downgraded)
nvme25n1        0a:00.0     Speed 32GT/s (ok)       Width x2 (downgraded)

$ cat tst.awk
BEGIN { FIELDWIDTHS="16 12 24 *" }
NR != 2 {
    print
    for (i=1; i<=NF; i++) {
        gsub(/^ *| *$/,"",$i)
        print "\t" i, "<" $i ">"
    }
    print "-----"
}

$ awk -f tst.awk file
nvmeSerial      Endpoint        nvmeSpeed           nvmeWidth
        1 <nvmeSerial>
        2 <Endpoint>
        3 <nvmeSpeed>
        4 <nvmeWidth>
-----
nvme0n1         c7:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
        1 <nvme0n1>
        2 <c7:00.0>
        3 <Speed 8GT/s (ok)>
        4 <Width x2 (downgraded)>
-----
nvme1n1         c8:00.0     Speed 8GT/s (ok)        Width x2 (downgraded)
        1 <nvme1n1>
        2 <c8:00.0>
        3 <Speed 8GT/s (ok)>
        4 <Width x2 (downgraded)>
-----
nvme15n1        48:00.0                             Width x2 (downgraded)
        1 <nvme15n1>
        2 <48:00.0>
        3 <>
        4 <Width x2 (downgraded)>
-----
nvme25n1        0a:00.0     Speed 32GT/s (ok)       Width x2 (downgraded)
        1 <nvme25n1>
        2 <0a:00.0>
        3 <Speed 32GT/s (ok)>
        4 <Width x2 (downgraded)>
-----

and then it's trivial to select the part of the 3rd field you want to print. e.g. using gensub() as I did. The above will work no matter which fields are missing from any line, how many words are in any field, etc.

Ed Morton
  • 31,617
2

Another method assuming fixed width fields (only extracts one of the fields, and assumes that it always starts with the 6 chars "Speed " if it's not empty):

cut -c35-52 file | sed '1,2d;s/ .*//'

Or match " Speed ":

awk -F ' Speed +' 'NR>2 {sub(/ .*/,"",$2); print $2}' file
sed -E '1,2d;s/.* Speed +([^ ]+).*/\1/;t;c\\' file
perl -nE 'say m{\sSpeed\s+(\S+)} if $.>2' file
rowboat
  • 2,791
0
 awk 'NR>2{if($4 ~ /^[0-9].*GT/){print $1" =======>" $4}else{if($4 !~ /^[0-9].*GT/){print $1"==================== doesnt contain speed==========================="}}}' filename

output

nvme0n1 =======>8GT/s
nvme1n1 =======>8GT/s
nvme2n1 =======>8GT/s
nvme3n1 =======>8GT/s
nvme4n1 =======>8GT/s
nvme5n1 =======>8GT/s
nvme6n1 =======>8GT/s
nvme7n1 =======>8GT/s
nvme8n1 =======>8GT/s
nvme9n1 =======>8GT/s
nvme10n1 =======>8GT/s
nvme11n1 =======>8GT/s
nvme12n1 =======>8GT/s
nvme13n1 =======>8GT/s
nvme14n1 =======>8GT/s
nvme15n1==================== doesnt contain speed===========================
nvme16n1 =======>8GT/s
nvme17n1 =======>8GT/s
nvme18n1 =======>8GT/s
nvme19n1 =======>8GT/s
nvme20n1 =======>8GT/s
nvme21n1 =======>8GT/s
nvme22n1 =======>8GT/s
nvme23n1 =======>8GT/s
nvme24n1 =======>8GT/s
nvme25n1 =======>32GT/s