0

I have the following bash script:

#!/bin/bash

OUTPUT=NULL,2,3,NULL,5,6,7,8

pvar1=$(echo $OUTPUT | awk -F ',' '{print $1}') pvar2=$(echo $OUTPUT | awk -F ',' '{print $2}') pvar3=$(echo $OUTPUT | awk -F ',' '{print $3}') pvar4=$(echo $OUTPUT | awk -F ',' '{print $4}') pvar5=$(echo $OUTPUT | awk -F ',' '{print $5}') pvar6=$(echo $OUTPUT | awk -F ',' '{print $6}') pvar7=$(echo $OUTPUT | awk -F ',' '{print $7}') pvar8=$(echo $OUTPUT | awk -F ',' '{print $8}')

for i in {$pvar1..$pvar8} do

case "$i" in

    "NULL")
            pOUT="$(awk '{ $* = $* }1' FS=, OFS=, <<<"$OUTPUT")"
            ;;
    "")
            echo "particulars field is not allowed to be empty. Run the program again and enter NULL or choose a value!" && exit 0
            ;;
    *)
            pOUT="$(awk '{ $* = q $* q }1' FS=, OFS=, q="'"  <<<"$OUTPUT")"
            ;;

esac

done

echo "$pOUT"

The output should be:

pOUT=NULL,'2','3',NULL,'5','6','7','8'

And the script should exit if no value is provided, e.g.:

OUTPUT=NULL,2,3,NULL,5,6,,8

I am not sure how to write the awk commands in a for loop. Can someone please help?

3 Answers3

1

Running awk to split one(!) field or join characters is wasteful and unnecessary; shell can do this fine. It's not clear exactly what you want because some of your code makes no sense at all, but based on your single example this should be close:

#!/bin/bash
OUTPUT=NULL,2,3,NULL,5,6,7,8

IFS=,; set -f; set -- $OUTPUT; out= for i in "$@"; do case "$i" in # you can omit in "$@" as the default, but this is clearer "") echo empty input not allowed; exit 0;; # may want nonzero status for error NULL) out="$out,$i";; *) out="$out,'$i'";; esac; done echo "${out:1}" # bash only; or "${out#,}" on any Bourne/POSIX shell

this leaves IFS and noglob set; for a standalone script this doesn't matter,

but if source'd or entered manually you probably need to either reset these

explicitly or (more easily) put the whole thing in parentheses (a subshell)

but if you do want to use awk it can easily do the whole job at once

echo NULL,2,3,NULL,5,6,7,8 |\
awk -F, -vq=\' '{for(i=1;i<=NF;i++)if($i==""){print "empty not allowed";exit 0}
  else if($i=="NULL")out=out","$i; else out=out","q$(i)q; print substr(out,2)}'
# linebreak in script for clarity, may be omitted
# since there's only one line=record input, you _could_ 
# replace the for(...) with while(++i<=NF) but I don't like it

or even easier, perl:

echo NULL,2,3,NULL,5,6,7,8 |\
perl -lne 'print join ",",map{ length or die "empty not allowed"; $_=="NULL"?$_:"\047".$_."\047"}split(",")'
0

Don't know what you're trying to achieve here, but the awk in a loop is probably not what is going wrong.

for i in {$pvar1..$pvar8}
do
    echo $i
done

will give you

{NULL..8}

Which is probably not what you want.

An option would be:

f=0
while [ $f -lt 8 ] ; do 
    f=$((f+1))
    fv=$(echo $OUTPUT | awk -F ',' "{print \$$f}")
    if [ "$fv" = "" ] ; then
        echo "$f is empty; run the program again"
        exit 0
    fi
done
echo "pOUT=$pOUT"

which more or less behaves as you described. Instead of awk, you could also look at cut, but that depends on what you are doing further with the script.

Ljm Dullaart
  • 2,788
0

{$pvar1..$pvar8} does not work as you expect.

Instead of populating pvar1, pvar2 and so on, store information in positional parameters:

# parse the original $@ first and save what you want from it

OUTPUT=NULL,2,3,NULL,5,6,7,8

old_IFS="$IFS" IFS=, set -f set -- $OUTPUT IFS="$old_IFS"

Then it's easy to iterate. Example:

for i
do
   printf '%s\n' "$i"
done

In addition you have the number of fields ($#) for free. The code is portable.

There's a flaw though. Unset IFS is not equivalent to an empty IFS. After IFS="$old_IFS" the variable will be set, even if the original one was unset. Hopefully you know what IFS the script as a whole should use, so you can adjust the code. Adding some logic that tests if the relevant variable is set before assigning its value to another variable is also a possibility.

In Bash you can avoid overwriting the original array of positional parameters by working with a named array variable.

#!/bin/bash

OUTPUT=NULL,2,3,NULL,5,6,7,8

IFS=, read -ra myarray <<<"$OUTPUT"

for i in "${myarray[@]}" do printf '%s\n' "$i" done


In your loop, if awk commands were working, pOUT=… would assign something to the variable without saving what has already been done; previous pOUT is not used. This means the final pOUT would come from the last iteration only. I think you don't want pOUT to depend on the final field of OUTPUT; you want each part of pOUT to depend on the corresponding part of OUTPUT.

Build pOUT sequentially:

#!/bin/bash

OUTPUT=NULL,2,3,NULL,5,6,7,8

IFS=, read -ra myarray <<<"$OUTPUT"

pOUT="" for i in "${myarray[@]}" do case "$i" in "NULL") pOUT="$pOUT,$i" ;; "") echo "particulars field is not allowed to be empty. Run the program again and enter NULL or choose a value!" && exit 0 ;; *) pOUT="$pOUT,'$i'" ;; esac done pOUT="${pOUT#,}" printf 'pOUT=%s\n' "$pOUT"

Note this method generates a leading , in pOUT. After the loop we get rid of it with ${pOUT#,}.