3

Inside file.txt

chicken sheep cow  
tomato cucumber  
banana

Without if statement

while read -r column1 column2 column3; do  
    command  
done < file.txt

Using if statement, how to if the line has three columns, it do command1, if it has two columns, do command2 and if only has one column, it do command3?

  • (What if there are more than three? That read would concatenate the fourth and so on with the third one.) – ilkkachu Oct 29 '17 at 22:50
3

Or other approach with smallest difference of your example:

#!/bin/bash

while read -r column1 column2 column3; do
        if [ -z "$column2" ] ; then
                printf '%s\n' "Only first column has data"
        elif [ -z "$column3" ]; then
                printf '%s\n' "Only first and second columns has data"
        elif [ -n "$column3" ]; then
                printf '%s\n' "All three columns has data"
        fi
done < file.txt

Output will be:

All three columns has data
Only first and second columns has data
Only first column has data

Notes:

In your example first and second lines contains several spaces at the end, but by default read removes all leading and trailing space characters.

If your input contains more than 3 columns, all data in third column and further will placed in column3

See How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?

  • You need to quote the variables in the tests: [ -n "$var" ] etc. With -z it works without the quotes here, slightly accidentally, since if $var is empty in [ -z $var ], the expansion is just [ -z ] which just tests if -z is a nonempty string. The one with -n, however, always gives a true result. (Of course whitespace and glob characters in the variable also cause a mess if the vars are unquoted.) – ilkkachu Oct 29 '17 at 22:48
  • @ilkkachu, thx. It's fair remark, answer was changed – Egor Vasilyev Oct 30 '17 at 5:22
1

You may read each line into array with read -ra, then, check the array size:

fmt="Number of fields: %s. The last one: %s\n"
while read -ra items; do 
    if [ ${#items[*]} == 3 ]; then 
        printf "$fmt" ${#items[*]} ${items[-1]}
    elif [ ${#items[*]} == 2 ]; then 
        printf "$fmt" ${#items[*]} ${items[-1]}
    elif [ ${#items[*]} == 1 ]; then
        printf "$fmt" ${#items[*]} ${items[-1]}
    fi
done < file.txt

Of course, expression printf "$fmt" ${#items[*]} ${items[-1]} was used just for demonstration, you can define your own.


The above approach outputs(as an example):

Number of fields: 3. The last one: cow
Number of fields: 2. The last one: cucumber
Number of fields: 1. The last one: banana
1
while read -r column1 column2 column3; do
    if [ -z "$column2" ]; then
        # one column
        : command
    elif [ -z "$column3" ]; then
        # two columns
        : command
    else
        # three columns
        : command
    fi
done < file.txt

or

while read -r column1 column2 column3; do
    set -- $column1 $column2 $column3
    case $# in
        1)
            : command
        ;;
        2)
            : command
        ;;
        *)
            : command
        ;;
    esac
done < file.txt
0

Hauke Laging's answer already has a good idea of using evaluation via setting positional parameters and evaluating their number on each iteration. Variation on the theme can be done with use of arrays in bash or ksh ( which also can be handy in case we do want to keep positional parameters). The below example is for bash ( note that for ksh or mksh you'd use -A insead of -a in read, and of course change #! line):

#!/usr/bin/env bash
line_counter=1
while read -r -a arr;
do
    case "${#arr[@]}" in
        1) printf "One column in line %d\n" "$line_counter";;
        2) printf "Two columns in line %d\n" "$line_counter";;
        3) printf "Three columns in line %d\n" "$line_counter";;
        *) printf "More than 3 lines in line %d\n" "$line_counter";;
    esac
    ((line_counter++))
done < input.txt

And the output is like so:

$ ./eval_columns.sh                                                                                                                                                                  
Three columns in line 1
Two columns in line 2
One column in line 3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.