我有以下 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}
没有按预期工作。
不要填充等pvar1
,pvar2
而是将信息存储在位置参数中:
# 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
并不等同于空IFS
。IFS="$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#,}
。