在 for 循环中使用 awk 命令的 Bash case 语句不起作用

在 for 循环中使用 awk 命令的 Bash case 语句不起作用

我有以下 bash 脚本:

#!/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"

输出应为:

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

如果没有提供任何值,脚本应该退出,例如:

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

我不确定如何在 for 循环中编写 awk 命令。有人可以帮忙吗?

答案1

运行 awk 来拆分一个 (!) 字段或连接字符是浪费且不必要的;shell 可以很好地完成这项工作。不清楚您到底想要什么,因为您的某些代码根本没有意义,但根据您的单个示例,这应该很接近:

#!/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)

但如果你确实想使用 awk,它可以轻松地一次性完成整个工作

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

或者更简单的,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(",")'

答案2

不知道您在这里想要实现什么,但是循环中的 awk 可能不是出错的地方。

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

会给你

{NULL..8}

这可能不是您想要的。

一种选择是:

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"

其行为或多或少与您描述的一样。除了awk,您还可以查看cut,但这取决于您接下来要对脚本执行的操作。

答案3

{$pvar1..$pvar8} 没有按预期工作

不要填充等pvar1pvar2而是将信息存储在位置参数中:

# 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"

然后迭代就很容易了。例如:

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

此外,您还可以免费获得字段数量($#)。代码是可移植的。

不过,有一个缺陷。取消设置IFS并不等同于空IFSIFS="$old_IFS"即使原始变量未设置,之后变量也会被设置。希望您知道IFS整个脚本应该使用什么,这样您就可以调整代码。在将相关变量的值赋给另一个变量之前,添加一些测试相关变量是否已设置的逻辑也是可行的。

在 Bash 中,您可以通过使用命名数组变量来避免覆盖原始位置参数数组。

#!/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

在您的循环中,如果awk命令正在运行,pOUT=…则会将某些内容分配给变量而不保存已完成的操作;pOUT不使用 previous。这意味着 finalpOUT仅来自最后一次迭代。我认为您不想pOUT依赖于 的 final 字段OUTPUT;您希望 的每个部分pOUT都依赖于 的相应部分OUTPUT

依次构建pOUT

#!/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"

注意,此方法会生成一个前导,pOUT循环结束后,我们用 将其去除${pOUT#,}

相关内容