2

I have a program which transposes a matrix. It works properly when passed a file as a parameter, but it gives strange output when given input via stdin.

This works:

$ cat m1
1   2   3   4
5   6   7   8

$ ./matrix transpose m1
1   5   
2   6   
3   7   
4   8

This doesn't:

$ cat m1 | ./matrix transpose
5
[newline]
[newline]
[newline]

This is the code I'm using to transpose the matrix:

function transpose {
    # Set file to be argument 1 or stdin 
    FILE="${1:-/dev/stdin}"
    if [[ $# -gt 1 ]]; then
        print_stderr "Too many arguments. Exiting."
        exit 1
    elif ! [[ -r $FILE ]]; then
        print_stderr "File not found. Exiting."
        exit 1
    else
        col=1
        read -r line < $FILE
        for num in $line; do
            cut -f$col $FILE | tr '\n' '\t'
            ((col++))
            echo
        done
        exit 0
    fi
}

And this code handles the argument passing:

# Main
COMMAND=$1
if func_exists $COMMAND; then
    $COMMAND "${@:2}"
else
    print_stderr "Command \"$COMMAND\" not found. Exiting."
    exit 1
fi

I'm aware of this answer but I can't figure out where I've gone wrong. Any ideas?

Alex Johnson
  • 958
  • 8
  • 23

1 Answers1

2
for num in $line; do
    cut -f$col $FILE | tr '\n' '\t'
    ((col++))
    echo
done

This loop reads $FILE over and over, once for each column. That works fine for a file but isn't suitable for stdin, which is a stream of data that can only be read once.

A quick fix would be to read the file into memory and use <<< to pass it to read and cut.

matrix=$(< "$FILE")
read -r line <<< "$matrix"
for num in $line; do
    cut -f$col <<< "$matrix" | tr '\n' '\t'
    ((col++))
    echo
done

See An efficient way to transpose a file in Bash for a variety of more efficient one-pass solutions.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578