error
虽然不把shell当作主力的编程语言,但是在任务中,难免不会用到shell,尤其是拿shell来做为各种程序之间的粘合剂,或者那shell直接做一些简单的任务,或者定时任务。
shell功能强大,代码简省,如果写的合适,shell的性能不输其他专门的脚本语言,但是呢,遇到问题,也是头疼。故,本文的目的是,尽可能多的记录工作中遇到的问题。方便,自己。
问题列表
{0,1}解析
sh myshell.sh 最好自己指定使用哪个shell,可能不同的shell之间略微有差别。
for db in {0,1};do
for table in `seq 0 1`;do
outsql=marked_ship_$db_$table.sql
echo "ordercenter_order_tag_"$db marked_ship_$table to $outsql
done
done
宿主机上直接执行sh mysh.sh 实际/usr/bin/sh
ordercenter_order_tag_0 marked_ship_0 to marked_ship_0.sql
ordercenter_order_tag_0 marked_ship_1 to marked_ship_1.sql
ordercenter_order_tag_1 marked_ship_0 to marked_ship_0.sql
ordercenter_order_tag_1 marked_ship_1 to marked_ship_1.sql
docker mysql 执行sh mysh.sh 实际/bin/sh
ordercenter_order_tag_{0,1} marked_ship_0 to marked_ship_0.sql
ordercenter_order_tag_{0,1} marked_ship_1 to marked_ship_1.sql
直接bash执行 方一致。貌似因为执行的
echo $SHELL
which sh # 查看哪个sh
crontab -e出错
问题类似于下面这个文章:
http://blog.sina.com.cn/s/blog_633685790102vms0.html
报错的时候:
crontab -e
no crontab for dxt - using an empty one
/tmp/crontab.XXXX9MaMQk: 权限不够
大概是因为/tmp文件夹的权限问题。故,直接:
chmod 777 /tmp
定时任务,我个人觉得特别难编写,主要是因为,1、执行的时候,先cd到目录,最稳妥,防止引入了相对变量的文件路径。2、加上环境变量,尤其是PATH,有时,居然连/usr/local/bin都不在环境变量中。故,最好自己设置一下。3、测试,测试的时候,跳到cd /路径,再执行,看看能否正常执行。
heredoc
为中文变量复制,正确的用法如下
query_sql=$(cat <<EOL
SELECT
COUNT (DISTINCT ship_id)
FROM
tb_scan
WHERE
ins_db_tm >= '2021-09-24'
AND ins_db_tm < '2021-09-25'
AND scan_typ = '14'
AND scan_site = '';
EOL
)
echo $query_sql
说明:EOL的括号,要另起一行,避免导致结束符号失效。另外,echo,输出的内容,换行符号预产被去掉了,即只有一行。
heredoc要配合cat才能使用,query_sql=<<EOL,直接为变量赋值的格式,结果,输出的是一个空行。
query_sql=$(
echo $query_sql |
sed "s/#START_DATE/$START_DATE/g"|
sed "s/#END_DATE/$END_DATE/g" |
sed "s/#SCAN_SITE/$SCAN_SITE/g"
)
还能按如下的方式,来拼接管道。
cat <<'EOF' | sed 's/i/m/g' > linuxidc.txt
ssh时,使用heredoc
ssh -T user@host.com << EOF
文件名中有空格
for each in *.flv;do
grep "$each" downlist.txt > /dev/null
if [ $? -ne 0 ];then
echo $each
sz "$each"
fi
done
说明如下:
in后面直接跟模糊查找的名称,这样,而不是使用两个反引号或者
$(ls *.flv)等符号,这是因为,in的属性,会默认将包含空格的部分,切分成两部分,而文件名中,正好又包含了空格等符号。grep命令中,使用双引号包裹住each变量,这是因为,如果不包裹,则each变量中因包含空格,而被分隔成两部分。下面的sz命令相同。
这应该说是shell中针对空格中的坑。
echo * 问题
问题描述:
SQL="
copy (SELECT * FROM tu_doc_info WHERE dtime >= '${DAY_AGO_10}' and doc_sn = '${SHIP_ID}' order by dtime ) to '${TMPDIR}${SHIP_ID}_ludan.csv' with csv header;
"
SQL=$(cat <<EOL
copy (SELECT * FROM tu_doc_info WHERE dtime >= '${DAY_AGO_10}' and doc_sn = '${SHIP_ID}' order by dtime ) to '${TMPDIR}${SHIP_ID}_ludan.csv' with csv header;
EOL
)
START='*'
SQL=$(cat <<EOL
copy (SELECT ${START} FROM tu_doc_info WHERE dtime >= '${DAY_AGO_10}' and doc_sn = '${SHIP_ID}' order by dtime ) to '${TMPDIR}${SHIP_ID}_ludan.csv' with csv header;
EOL
)
echo $SQL
有如上的3种写法,在echo的时候,sql的*都会被当前执行shell的工作路径下的,文件列表所替代。这本来是echo的一个强大特色。
但是,不要慌,echo出来的,是被echo加工了,*交给psql执行的时候,不会被替换成文件列表。
解决方式非常简单:如下:
echo "$SQL"
原因:echo 后面的变量,如果缺少了双引号包裹,则,变相的,拆分成了n个参数,echo * 确实会输出文件列表。
命令 * 错误2
问题基本上等同上个问题,但是隐藏性更深一些。先说如何正确的调用:
#!/bin/bash
day=$1
gp_file_name=$2
csv_path=$3
csv_name="$4"
for each in `ls $csv_path/$day/$csv_name`;do
echo $each
psql -d develop -h localhost -U gpadmin -p 5432 -c "\copy $gp_file_name from $each with csv";
if [ $? -ne 0 ];then
echo "error: import $each"
fi
done
正确调用:
./day_file_without_header.sh 20211024_2 cyf_etl_dta_20211026 /etl/dta "tu_doc_info*.csv"
错误调用:
./day_file_without_header.sh 20211024_2 cyf_etl_dta_20211026 /etl/dta tu_doc_info*.csv
区别就是,最后一个带*好的参数,其实在调用命令时,参数应该被bash给解析了,解析成当前目录下的文件。
有点像我常用的命令行参数:
sz *.flv
其实,就是bash在我调用参数的时候,帮我给解析了。
总结:调用命令行参数的时候,当我们希望bash帮我们解析参数的时候,做额外的工作时,我们就不加引号,需要包含我们的参数的时候,不希望bash做额外工作时,就加引号(单双均可)保护。
ls "$csv_path/$day/$csv_name" 这种被保护起来了,bash就不会帮我们做额外的工作,但又不是我们想要的结果。
单引号保护
./crud_zh.sh
#!/bin/bash
# 时间:2022-02-21 10:13
# 描述:yii2自动生成的代码,汉化
if [ -z "$1" ];then
echo "$0 ../cloud/backend/views/netrange/*.php"
exit
fi
sed -i 's#<h1><?= Html::encode($this->title) ?></h1>##' $1
sed -i 's#Update#更新#' $1
sed -i 's#Delete#删除#' $1
sed -i 's#Are you sure you want to delete this item?#您确定要删除该条记录吗?#' $1
sed -i 's#Create#新增#' $1
sed -i 's#Save#保存#' $1
调用
./crud_zh.sh '../cloud/backend/views/warning-order/*.php'
错误调用
./crud_zh.sh ../cloud/backend/views/warning-order/*.php
说明:没有单引号保护时,crud_zh.sh,将*解析调,那么,*被解析成多个具体的文件,可能传入多个参数到./crud_zh.sh,而到sed的命令中,$1只是第一个文件。
unix换行
两种方式,一种是使用vim,设置并保存,另外一种,更简单一些,使用sed批量替换。
出现的另外一种原因是:git设置,会自动的转换换行符号。切记。
sed -i 's/\r$//' for_lua.sh
# vim
set ff=unix
set fileformat=unix # 全名