Organize multiple lines of text, every third or second line, depending on initial string variable

Viewed 108

I have an output of lines generated as such:

LINES=$(for i in {scsi,sat,ata,nvme}; do smartctl --scan -d $i ; done | sort | uniq | grep -v '^#|/dev/sd' | awk '{print$2,$3,$5}' | sed 's/,\r$//') ; while IFS= read -r line; do smartctl -i $line; done <<< "$LINES" | grep -v Family | grep -e Model -e Product: -e Vendor: -e "User Capacity" -e "Total NVM Capacity" | awk -F"[:\t]" '{print$2}'

Giving the following output:

           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
 INTEL SSDSC2KB240G7
240,057,409,536 bytes [240 GB]
                   INTEL SSDPE2KX020T7
             2,000,398,934,016 [2.00 TB]
                   INTEL SSDPE2KX020T7
             2,000,398,934,016 [2.00 TB]

I'm hoping to put each HDD information into it's own line, while simultaneously parsing the Capacity to display it's human-readable format. It's ok if the capacitt still has [brackets]. Desired output:

SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
INTEL SSDSC2KB240G7 240 GB
INTEL SSDPE2KX020T7 2.00 TB
INTEL SSDPE2KX020T7 2.00 TB
3 Answers

Could you please try following, written and tested with shown samples only in GNU awk.

awk -v RS= -v FS="\n" '
{
  while($0){
    match($0,/[^]]*/)
    val=""
    val=substr($0,RSTART,RLENGTH)
    gsub(/\n[[:space:]]+|\n/," ",val)
    sub(/^ +/,"",val)
    num=split(val,arr," |\\[")
    print arr[1],arr[2],arr[num-1],arr[num]
    $0=substr($0,RSTART+RLENGTH+1)
  }
}'  Input_file

Explanation: Adding detailed explanation for above.

awk -v RS= -v FS="\n" '                        ##Starting awk program from here and setting field separator as new line and RS as null.
{
  while($0){                                   ##Running while loop till line exists.
    match($0,/[^]]*/)                          ##Using match function to match everything till ] here.
    val=""                                     ##Nullifying variable val here.
    val=substr($0,RSTART,RLENGTH)              ##Creating val which has sub-string from RSTART to till RLENGTH value.
    gsub(/\n[[:space:]]+|\n/," ",val)          ##Globally substituting either newline spaces OR only new lines with space in val.
    sub(/^ +/,"",val)                          ##Substituting starting spaces with NULL in val here.
    num=split(val,arr," |\\[")                 ##Splitting val value into arr with deliminator of space and [ for all values.
    print arr[1],arr[2],arr[num-1],arr[num]    ##printing 1st, 2nd and 2nd last and last value of arr here.
   $0=substr($0,RSTART+RLENGTH+1)              ##Re-creating value of current line which has REST of the line leaving part which was matched above, to skip already matched part and avoid duplicates.
  }
}'  Input_file                                 ##Mentioning Input_file name here.

In case you are running this command by taking output of another commands then pipe this to them like your_command | awk... you could remove Input_file from above then.

awk -v RS=[][] '{ORS=(NR%2? " ": "\n"); print $1, $2}' file

Testing with your input:

> cat file
          SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
           SEAGATE
          ST2000NX0273
    2,000,398,934,016 bytes [2.00 TB]
 INTEL SSDSC2KB240G7
240,057,409,536 bytes [240 GB]
                   INTEL SSDPE2KX020T7
             2,000,398,934,016 [2.00 TB]
                   INTEL SSDPE2KX020T7
             2,000,398,934,016 [2.00 TB]

Output:

SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
INTEL SSDSC2KB240G7 240 GB
INTEL SSDPE2KX020T7 2.00 TB
INTEL SSDPE2KX020T7 2.00 TB

Also, the initial approach here, as ] is the end of every "record", would be something like this:

awk -v RS=] '{gsub("\\[",""); print $1, $2, $(NF-1), $NF}' file

Using any awk in any shell on every Unix box:

$ cat tst.awk
gsub(/.*[[]|[]]/,"") {
    $0 = prev FS $0
    $1 = $1
    print
    prev = ""
    next
}
{ prev = prev FS $0 }

.

$ awk -f tst.awk file
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
SEAGATE ST2000NX0273 2.00 TB
INTEL SSDSC2KB240G7 240 GB
INTEL SSDPE2KX020T7 2.00 TB
INTEL SSDPE2KX020T7 2.00 TB
Related