zizala
zizala

Reputation: 11

Bash script, command - output to array, then print to file

I need advice on how to achieve this output:

myoutputfile.txt
    Tom Hagen 1892
    State: Canada
    Hank Moody 1555
    State: Cuba
    J.Lo 156
    State: France

output of mycommand:

/usr/bin/mycommand
        Tom Hagen
        1892
        Canada
        Hank Moody
        1555
        Cuba
        J.Lo
        156
        France

Im trying to achieve with this shell script:

IFS=$'\r\n' GLOBIGNORE='*' :; names=( $(/usr/bin/mycommand) )
for name in ${names[@]}
do
#echo $name
echo ${name[0]} 
#echo ${name:0} 
done

Thanks

Upvotes: 1

Views: 91

Answers (4)

mklement0
mklement0

Reputation: 437110

  • chepner's pure bash solution is simple and elegant, but slow with large input files (loops in bash are slow).
  • Michael Jaros' solution is even simpler, if you have GNU xargs (verify with xargs --version), but also does not perform well with large input files (external utility printf is called once for every 3 input lines).

If performance matters, try the following awk solution:

/usr/bin/mycommand | awk '
 { ORS = (NR % 3 == 1 ? " " : "\n")
  gsub("^[[:blank:]]+|[[:blank:]]*\r?$", "") } 
 { print (NR % 3 == 0 ? "State: " : "") $0 }
' > myoutputfile.txt
  • NR % 3 returns the 0-based index of each input line within its respective group of consecutive 3 lines; returns 1 for the 1st line, 2 for the 2nd, and 0(!) for the 3rd.
  • { ORS = (NR % 3 == 1 ? " " : "\n") determines ORS, the output-record separator, based on that index: a space for line 1, and a newline for lines 2 and 3; the space ensures that line 2 is appended to line 1 with a space when using print.
  • gsub("^[[:blank:]]+|[[:blank:]]*\r?$", "") strips leading and trailing whitespace from the line - including, if present, a trailing \r, which your input seems to have.
  • { print (NR % 3 == 0 ? "State: " : "") $0 } prints the trimmed input line, prefixed by "State: " only for every 3rd input line, and implicitly followed by ORS (due to use of print).

Upvotes: 1

Michael Jaros
Michael Jaros

Reputation: 4681

An easy one-liner (not tuned for performance):

/usr/bin/mycommand | xargs -d '\n' -L3 printf "%s %s\nState: %s\n"

It reads 3 lines at a time from the pipe and then passes them to a new instance of printf which is used to format the output.

If you have whitespace at the beginning (it looks like that in your example output), you may need to use something like this:

/usr/bin/mycommand | sed -e 's/^\s*//g' | xargs -d '\n' -L3 printf "%s %s\nState: %s\n"

Upvotes: 2

ColOfAbRiX
ColOfAbRiX

Reputation: 1059

#!/bin/bash

COUNTER=0
/usr/bin/mycommand | while read LINE
do

    if [ $COUNTER = 0 ]; then
        NAME="$LINE"
        COUNTER=$(($COUNTER + 1))

    elif [ $COUNTER = 1 ]; then
        YEAR="$LINE"
        COUNTER=$(($COUNTER + 1))

    elif [ $COUNTER = 2 ]; then
        STATE="$LINE"
        COUNTER=0

        echo "$NAME $YEAR"
        echo "State: $STATE"
    fi

done

Upvotes: 1

chepner
chepner

Reputation: 530920

Assuming you can always rely on the command to output groups of 3 lines, one option might be

/usr/bin/mycommand | 
  while read name;
        read year;
        read state; do
    echo "$name $year"
    echo "State: $state"
  done

An array isn't really necessary here.

One improvement could be to exit the loop if you don't get all three required lines:

while read name && read year && read state; do
    # Guaranteed that name, year, and state are all set
    ...
done

Upvotes: 3

Related Questions