场景需求(简化): 要求开发一个批量更新MySQL表字段的shell脚本,可以一个或多个表名作为传参,在脚本里放在where条件里面。
vn_tb_name=$1
vn_date=$2
mysql -e "update tb_table_info set date_no=${vn_date}
where sys_table_name in (${vn_tb_name}) "
希望的效果:
sh update_mysql_date.sh AAAA,BBBB,CCCC 20201201
输出:
update tb_table_info set date_no=20201201
where sys_table_name in ("AAAA","BBBB","CCCC")
难点分析:shell 传参以空格为分隔符,所以传多个值作为一个参数,可以用其他分隔符串起来,可以用awk提取为每个表增加引号,但如何保留(或者修改)分隔符?。
解决一:指定格式。在输入表名参数时,强制要求按" 'AAAA','BBBB','CCCC' "输入
sh update_mysql_date.sh "'AAAA','BBBB','CCCC'" 20201201
这种形式简单,不用任何处理,但输入要求较为繁琐。
解决二:使用awk处理,for循环,用\转义“(注意这里用的是printf,而不是print,不希望换行)。
echo "AAAA,BBBB,CCCC" | awk -F, '{for(i=1;i<=NF;i++) {printf "\""$i"\""}}'
输出:
"AAAA""BBBB""CCCC"
这里引号倒是加上了,但是中间没有逗号分割,传到MySQL依然用不了。
继续分析,分隔符被awk吃掉了,那我们就显示打印出来FS,于是乎
echo "AAAA,BBBB,CCCC" | awk -F, '{for(i=1;i<=NF;i++) {printf "\""$i"\""FS}}'
输出:
"AAAA","BBBB","CCCC",
头疼,最后又多了一个逗号,依然会有语法问题。到此陷入了停滞。
。。。挠掉几根头发之后心想,既然最后一个域值不需要打印逗号,那for循环条件i<=NF,改成i<NF如何,然后再拼接最后一个值?
echo "AAAA,BBBB,CCCC" | awk -F, '{for(i=1;i<NF;i++) {printf "\""$i"\""FS} {print "\""$NF"\""}}'
输出:
"AAAA","BBBB","CCCC"
总算实现了,继续优化一下下,可以指定分隔符,更具适用性:
echo "AAAA,BBBB,CCCC" | awk 'BEGIN{FS=",";OFS="|"} {for(i=1;i<NF;i++) {printf "\""$i"\""OFS} {print "\""$NF"\""}}'
输出:
"AAAA"|"BBBB"|"CCCC"
扩展awk内置变量:
| ARGC: 命令行参数个数 | NF: 浏览记录的域个数 |
|---|---|
| AGRV :命令行参数排列 | NR: 已读的记录数 |
| FS: 设置输入域分隔符,同- F选项 | OFS :输出域分隔符 |
| FILENAME: awk浏览的文件名 | ORS: 输出记录分隔符 |
| FNR: 浏览文件的记录数 | RS: 控制记录分隔符 |