shell脚本学习

Shell 脚本(shell script),是一种为 shell 编写的脚本程序。
业界所说的 shell 通常都是指 shell 脚本,但读者朋友要知道,shell 和 shell script 是两个不同的概念。
由于习惯的原因,简洁起见,本文出现的 “shell编程” 都是指 shell 脚本编程,不是指开发 shell 自身。

以上内容来源于runoob

上半年花了一个多月时间修改一个开源的BI项目(Redash),改完后,由于没有使用也就暂时告一段落,就在昨天,一个客户说想找一个开源的BI系统,我们就给他推荐这个工具,因为它简单、方便、快捷,而且也比较受欢迎。但是客户对Docker不熟悉,就给他大致的讲了一个怎么在 Windows搭建一个测试环境。后来,突然想看看Redash的Dockerfile文件,看到里面的执行文件:

1
2
ENTRYPOINT ["/app/bin/docker-entrypoint"]
CMD ["server"]

docker-entrypoint是一个shell脚本,但是因为自己对shell的使用还未入门,所以就着这个机会,去简单了解一下。我们先看一下docker-entrypoint中的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/bin/bash
set -e

celery_worker() {
WORKERS_COUNT=${WORKERS_COUNT:-2}
QUEUES=${QUEUES:-queries,scheduled_queries}
WORKER_EXTRA_OPTIONS=${WORKER_EXTRA_OPTIONS:-}

echo "Starting $WORKERS_COUNT workers for queues: $QUEUES..."
exec /usr/local/bin/celery worker --app=redash.worker -c$WORKERS_COUNT -Q$QUEUES -linfo --max-tasks-per-child=10 -Ofair $WORKER_EXTRA_OPTIONS
}

scheduler() {
echo "Starting RQ scheduler..."

exec /app/manage.py rq scheduler
}

dev_scheduler() {
echo "Starting dev RQ scheduler..."

exec watchmedo auto-restart --directory=./redash/ --pattern=*.py --recursive -- ./manage.py rq scheduler
}

worker() {
echo "Starting RQ worker..."

exec /app/manage.py rq worker $QUEUES
}

dev_worker() {
echo "Starting dev RQ worker..."

exec watchmedo auto-restart --directory=./redash/ --pattern=*.py --recursive -- ./manage.py rq worker $QUEUES
}

dev_celery_worker() {
WORKERS_COUNT=${WORKERS_COUNT:-2}
QUEUES=${QUEUES:-queries,scheduled_queries}

echo "Starting $WORKERS_COUNT workers for queues: $QUEUES..."

exec watchmedo auto-restart --directory=./redash/ --pattern=*.py --recursive -- /usr/local/bin/celery worker --app=redash.worker -c$WORKERS_COUNT -Q$QUEUES -linfo --max-tasks-per-child=10 -Ofair
}

server() {
# Recycle gunicorn workers every n-th request. See http://docs.gunicorn.org/en/stable/settings.html#max-requests for more details.
MAX_REQUESTS=${MAX_REQUESTS:-1000}
MAX_REQUESTS_JITTER=${MAX_REQUESTS_JITTER:-100}
exec /usr/local/bin/gunicorn -b 0.0.0.0:5000 --name redash -w${REDASH_WEB_WORKERS:-4} redash.wsgi:app --max-requests $MAX_REQUESTS --max-requests-jitter $MAX_REQUESTS_JITTER
}

create_db() {
exec /app/manage.py database create_tables
}

celery_healthcheck() {
exec /usr/local/bin/celery inspect ping --app=redash.worker -d celery@$HOSTNAME
}

rq_healthcheck() {
exec /app/manage.py rq healthcheck
}

help() {
echo "Redash Docker."
echo ""
echo "Usage:"
echo ""

echo "server -- start Redash server (with gunicorn)"
echo "celery_worker -- start Celery worker"
echo "dev_celery_worker -- start Celery worker process which picks up code changes and reloads"
echo "worker -- start a single RQ worker"
echo "dev_worker -- start a single RQ worker with code reloading"
echo "scheduler -- start an rq-scheduler instance"
echo "dev_scheduler -- start an rq-scheduler instance with code reloading"
echo "celery_healthcheck -- runs a Celery healthcheck. Useful for Docker's HEALTHCHECK mechanism."
echo "rq_healthcheck -- runs a RQ healthcheck that verifies that all local workers are active. Useful for Docker's HEALTHCHECK mechanism."
echo ""
echo "shell -- open shell"
echo "dev_server -- start Flask development server with debugger and auto reload"
echo "debug -- start Flask development server with remote debugger via ptvsd"
echo "create_db -- create database tables"
echo "manage -- CLI to manage redash"
echo "tests -- run tests"
}

tests() {
export REDASH_DATABASE_URL="postgresql://postgres@postgres/tests"

if [ $# -eq 0 ]; then
TEST_ARGS=tests/
else
TEST_ARGS=$@
fi
exec pytest $TEST_ARGS
}

case "$1" in
worker)
shift
worker
;;
server)
shift
server
;;
scheduler)
shift
scheduler
;;
dev_scheduler)
shift
dev_scheduler
;;
celery_worker)
shift
celery_worker
;;
dev_celery_worker)
shift
dev_celery_worker
;;
dev_worker)
shift
dev_worker
;;
rq_healthcheck)
shift
rq_healthcheck
;;
celery_healthcheck)
shift
celery_healthcheck
;;
dev_server)
export FLASK_DEBUG=1
exec /app/manage.py runserver --debugger --reload -h 0.0.0.0
;;
debug)
export FLASK_DEBUG=1
export REMOTE_DEBUG=1
exec /app/manage.py runserver --debugger --no-reload -h 0.0.0.0
;;
shell)
exec /app/manage.py shell
;;
create_db)
create_db
;;
manage)
shift
exec /app/manage.py $*
;;
tests)
shift
tests $@
;;
help)
shift
help
;;
*)
exec "$@"
;;
esac

#!/bin/bash的作用

我们可以再很多sh脚本里面看到#!/bin/bash这段代码,那么它有什么作用呢?shell是一种脚本命令语言,它有多种解析器,例如:/bin/csh、/bin/perl、/bin/bash、bin/sh等等,那么在在第一行加上#!/bin/bash,就是告诉系统这个脚本需要bin/bash解释器来执行。

set -e的作用

set -e的作用是:当命令的返回值为非零状态时,则立即退出脚本的执行,防止导致一个致命的错误,而这些错误本应该在之前就被处理掉。
set -e 命令用法总结如下:

  1. 当命令的返回值为非零状态时,则立即退出脚本的执行。
  2. 作用范围只限于脚本执行的当前进行,不作用于其创建的子进程(https://blog.csdn.net/fc34235/article/details/76598448 )。
  3. 另外,当想根据命令执行的返回值,输出对应的log时,最好不要采用set -e选项,而是通过配合exit 命令来达到输出log并退出执行的目的。
    shell 中的 set -e 和 set +e的区别

shell 函数

shell函数的定义格式如下:

1
2
3
4
5
[ function ] funname [()]
{
action;
[return int;]
}

函数定义可以带function funname() 定义,也可以直接funname() 定义,不带任何参数。函数返回值可加return也可以不加,不加则以最后一条命令运行结果作为返回值。

1
2
3
4
5
6
7
8
celery_worker() {
WORKERS_COUNT=${WORKERS_COUNT:-2}
QUEUES=${QUEUES:-queries,scheduled_queries}
WORKER_EXTRA_OPTIONS=${WORKER_EXTRA_OPTIONS:-}

echo "Starting $WORKERS_COUNT workers for queues: $QUEUES..."
exec /usr/local/bin/celery worker --app=redash.worker -c$WORKERS_COUNT -Q$QUEUES -linfo --max-tasks-per-child=10 -Ofair $WORKER_EXTRA_OPTIONS
}

shell变量

1
2
# 如果WORKERS_COUNT未定义或者为空字符串,则返回默认值,否则返回WORKERS_COUNT的值
WORKERS_COUNT=${WORKERS_COUNT:-2}

注意,变量名和等号之间不能有空格,而且还需要遵循如下规则:

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
  • 中间不能有空格,可以使用下划线(_)。
  • 不能使用标点符号。
  • 不能使用bash里的关键字(可用help命令查看保留关键字)。

使用一个定义过的变量,只要在变量名前面加美元符号即可。例如:

1
$WORKERS_COUNT或者${WORKERS_COUNT}

shell 参数

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n,
n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推
实例
以下实例我们向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";
为脚本设置可执行权限,并执行脚本,输出结果如下所示:

$ chmod +x test.sh
$ ./test.sh 1 2 3
Shell 传递参数实例!
执行的文件名:./test.sh
第一个参数为:1
第二个参数为:2
第三个参数为:3
1
2
3
4
5
6
7
8
9
$#	传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。
如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

这里直接copy runoob的内容

shell case 用法

1
2
3
4
5
6
7
8
9
10
11
12
13
case "$1" in
worker)
shift
worker
;;
help)
shift
help
;;
*)
exec "$@"
;;
esac

shift命令:在shell中,经常可能会遇到多参数传递,例如$1,$2,…$9,借助shift命令可以访问更多传递的参数
case语句:

  • 以case开头,esac结尾;
  • 取值后面必须为单词in
  • 匹配一个值与一个模式以”)”(右括号)结束
  • 双分号 “;;” 表示命令序列结束;
  • 默认模式使用”*)”表示,在不满足前面的模式后,执行默认模式后的命令
    示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    worker() {
    WORKERS_COUNT=${WORKERS_COUNT:-2}
    QUEUES=${QUEUES:-queries,scheduled_queries}
    WORKER_EXTRA_OPTIONS=${WORKER_EXTRA_OPTIONS:-}

    echo "Starting $WORKERS_COUNT workers for queues: $QUEUES..."
    echo "$WORKERS_COUNT"

    }

    help() {
    echo "Redash Docker."
    echo ""
    echo "Usage:"
    echo ""

    echo "server -- start Redash server (with gunicorn)"
    echo "celery_worker -- start Celery worker"
    echo "dev_celery_worker -- start Celery worker process which picks up code changes and reloads"
    echo "worker -- start a single RQ worker"
    echo "dev_worker -- start a single RQ worker with code reloading"
    echo "scheduler -- start an rq-scheduler instance"
    echo "dev_scheduler -- start an rq-scheduler instance with code reloading"
    echo "celery_healthcheck -- runs a Celery healthcheck. Useful for Docker's HEALTHCHECK mechanism."
    echo "rq_healthcheck -- runs a RQ healthcheck that verifies that all local workers are active. Useful for Docker's HEALTHCHECK mechanism."
    echo ""
    echo "shell -- open shell"
    echo "dev_server -- start Flask development server with debugger and auto reload"
    echo "debug -- start Flask development server with remote debugger via ptvsd"
    echo "create_db -- create database tables"
    echo "manage -- CLI to manage redash"
    echo "tests -- run tests"
    }


    case "$1" in
    worker)
    shift
    worker
    ;;
    help)
    shift
    help
    ;;
    *)
    exec "$@"
    ;;
    esac
    效果如下:
    1
    2
    3
    [root@VM_175_142_centos ~]# ./shell_echo.sh worker
    Starting 2 workers for queues: queries,scheduled_queries...
    2

结束

这里只是借助redash的sh脚本来简单了解了一下shell的一些常用命令及语法,当然,shell的强大之处肯定不止于此,后面再遇到的时候,再来学习。大家也可以去runoob系统的学习一下

You forgot to set the qrcode for Alipay. Please set it in _config.yml.
You forgot to set the qrcode for Wechat. Please set it in _config.yml.
You forgot to set the business and currency_code for Paypal. Please set it in _config.yml.
You forgot to set the url Patreon. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×