学习shell语法之前最好拥有一些linux基础知识,掌握一定的linux命令。
1. 第一个shell程序
在hello.sh中编写shell程序:
#!/bin/bash
# first command
echo hello world!
cat ~/hello.txt
其中hello.txt的文件内容为:
Hello world!1 Helao world!2 Hello world!3 Hello world!4 Hello world!5 Hello world!6 abc!7
这时,如果我们直接执行hello.sh文件,则报错:-bash: ./hello.sh: Permission denied
,原因是文件没有执行权限,此时执行chmod u+x hello.sh
赋予权限,如下:
[root@bigData01 test]# ./hello.sh
-bash: ./hello.sh: Permission denied
[root@bigData01 test]# chmod u+x ./hello.sh
[root@bigData01 test]# ./hello.sh
hello world!
Hello world!1
Helao world!2
Hello world!3
Hello world!4
Hello world!5
Hello world!6
abc!7
其实也可以通过bash ./hello.sh
或sh ./hello.sh
执行文件,因为在执行的时候前面指定bash
或者sh
,表示把hello.sh这个脚本中的内容作为参数直接传给了bash或者sh命令来执行,所以这个脚本有没有执行权限都无所谓了。
2. shell中的变量
shell中的变量不需要声明,初始化也不需要指定类型,shell是一门弱类型的语言。shell中变量的命名要求:只能使用数字、字母和下划线,且不能以数字开头。
2.1 变量赋值
变量赋值是通过"="进行赋值,在变量、等号和值之间不能出现空格!如下:(执行成功是没有反馈的,只有执行错误的报错)
[root@bigData01 test]# name=cql
[root@bigData01 test]# name= cql
bash: cql: command not found...
[root@bigData01 test]# name =cql
bash: name: command not found...
[root@bigData01 test]# name_=cql
[root@bigData01 test]# name2_=cql
[root@bigData01 test]# 1name2_=cql
bash: 1name2_=cql: command not found...
[root@bigData01 test]# 1name2$=cql
bash: 1name2$=cql: command not found...
2.2 变量输出
输出变量时使用echo
命令,参数格式为echo $<变量名>
,例如输出name
变量内容,就可以使用echo $name
命令,但如果要在变量内容后面无缝链接另一个字符串haha
,则需要使用命令echo ${name}haha
。如下:
[root@bigData01 test]# echo $name
cql
[root@bigData01 test]# echo ${name}
cql
[root@bigData01 test]# echo ${name}haha
cqlhaha
[root@bigData01 test]# echo $name haha
cql haha
2.3 变量分类
2.3.1 本地变量
本地变量的格式是VAR_NAME=VALUE
,其实就是我们刚才在shell中那样直接定义的变量,这种变量一般用于在shell脚本中定义一些临时变量,只对当前shell进程有效,关闭shell进程之后就消失了,对当前shell进程的子进程和其它shell进程无效。
举个例子就可以证明上述描述,在当前shell进程下定义本地变量name=cql
,然后在其他进程和当前进程的子进程中分别输出该变量,若变量不存在则可以证明。如下:
首先在当前进程定义本地变量,输出正常:
[root@bigData01 test]# name=cql
[root@bigData01 test]# echo $name
cql
在另一个新的shell进程中输出该变量内容,输出为空:
[root@bigData01 ~]# cd "/test"
[root@bigData01 test]# echo $name
进入当前shell进程的子进程中输出该变量内容,输出为空:
[root@bigData01 test]# bash
[root@bigData01 test]# echo $name
[root@bigData01 test]# exit
exit
2.3.2 环境变量
环境变量的定义格式为:export VAR_NAME=VALUE
,环境变量的这种格式主要用于设置临时环境变量,当你关闭当前shell进程之后环境变量就消失了,对子shell进程有效,对其它shell进程无效。如下:
[root@bigData01 test]# export age=18
[root@bigData01 test]# echo $age
18
[root@bigData01 test]# bash
[root@bigData01 test]# echo $name
[root@bigData01 test]# exit
exit
若要环境变量永久生效,就将export VAR_NAME=VALUE
写入/etc/profile文件中,并执行source /etc/profile
加载文件中的命令。
2.3.3 位置变量
用于接收给shell脚本传递的参数,类似与$1、$2、$3等等。举一个浅显易懂的例子:
首先,编写hello.sh文件内容为:
#!/bin/bash
echo $0
echo $1
echo $2
echo $3
执行shell脚本:
[root@bigData01 test]# hello.sh a b c d
./hello.sh
a
b
c
发现$0表示当前文件的名称,$1表示脚本后的第一个参数,$2表示脚本后的第二个参数,等等。理论上来说,脚本后面有多少个参数,在脚本中就可以通过$和角标获取对应参数的值。
2.3.4 特殊变量
$# 是传给脚本的参数个数
$@ 是传给脚本的所有参数的列表
$* 是以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个
$$ 是脚本运行的当前进程ID号
$? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误
shell脚本为:
#!/bin/bash
echo $0
echo $#
echo $@
echo $$
echo $?
执行结果如下:
[root@bigData01 test]# hello.sh a b c
./hello.sh
3
a b c
5346
0
2.4 变量与引号
2.4.1 单引号
输出时不解析单引号中的变量,如下:
[root@bigData01 test]# echo $name
cql
[root@bigData01 test]# echo '$name'
$name
2.4.2 双引号
输出时解析双引号中的变量,如下:
[root@bigData01 test]# echo $name
cql
[root@bigData01 test]# echo "$name"
cql
2.4.3 反引号
输出时解析单引号中的变量并执行,如下:
反引号与
$()
的效果一致。
[root@bigData01 /]# name=pwd
[root@bigData01 /]# echo $name
pwd
[root@bigData01 test]# echo `$name`
/test
[root@bigData01 test]# echo $($name)
/test
3. shell中的循环
3.1 for循环
- for循环格式1:
for((exp1; exp2; exp3))
do
statements
done
例如:
数学算式要用(())括住。
[root@bigData01 test]# cat hello.sh
#!/bin/bash
for((i=0;i<10;i++))
do
((sum+=i))
done
echo $sum
[root@bigData01 test]# hello.sh
45
- for循环格式2:
for variable in value_list
do
statements
done
例如:
[root@bigData01 test]# cat hello.sh
#!/bin/bash
# first
for i in 1 2 3 4 5 6 7 8 9
do
((sum+=i))
done
echo $sum
sum=0
# second
for i in {1..9}
do
((sum+=i))
done
echo $sum
sum=0
#third
for i in `seq 1 1 9`
do
((sum+=i))
done
echo $sum
[root@bigData01 test]# hello.sh
45
45
45
3.2 while循环
while循环格式为:
while condition
do
statements
done
例如:
[root@bigData01 test]# cat while.sh
#!/bin/bash
i=1
sum=0
while ((i <= 100))
do
((sum += i))
((i++))
done
echo "The sum is: $sum"
[root@bigData01 test]# while.sh
The sum is: 5050
4. shell中的if判断
if判断分为三个类型:单分支、双分支、多分支。
4.1 单分支(if语句)
它的语法格式为:
if condition
then
statement(s)
fi
例如:
[root@bigData01 test]# cat if.sh
#!/bin/bash
read a
read b
if (( $a == $b ))
then
echo "a和b相等"
fi
[root@bigData01 test]# bash if.sh
12
12
a和b相等
4.2 双分支(if else语句)
它的语法格式为:
if condition
then
statement1
else
statement2
fi
例如:
[root@bigData01 test]# cat if.sh
#!/bin/bash
read a
read b
if (( $a == $b ))
then
echo "a和b相等"
else
echo "a和b不相等,输入错误"
fi
[root@bigData01 test]# bash if.sh
12
13
a和b不相等,输入错误
4.3 多分枝(if elif else语句)
它的语法格式为:
if condition1
then
statement1
elif condition2
then
statement2
elif condition3
then
statement3
······
else
statementn
fi
例如:
[root@bigData01 test]# cat if.sh
#!/bin/bash
printf "Input integer number: "
read num
if ((num==1)); then
echo "Monday"
elif ((num==2)); then
echo "Tuesday"
elif ((num==3)); then
echo "Wednesday"
elif ((num==4)); then
echo "Thursday"
elif ((num==5)); then
echo "Friday"
elif ((num==6)); then
echo "Saturday"
elif ((num==7)); then
echo "Sunday"
else
echo "error"
fi
[root@bigData01 test]# bash if.sh
Input integer number: 5
Friday
5. shell后台执行
若需要在后台执行shell脚本,则需要在命令后加&
字符,例如sh while2.sh &
。
但这样操作,当关闭shell窗口后文件将停止运行,因此若要使该程序永久在后台运行,则使用nohup
命令,如下:
[root@bigData01 test]# cat while.sh
#!/bin/bash
while ((1>0))
do
# echo "yes"
sleep 1
done
[root@bigData01 test]# while.sh
^C
[root@bigData01 test]# while.sh &
[1] 2700
[root@bigData01 test]# ps -ef | grep while.sh
root 2700 2612 0 10:45 pts/1 00:00:00 /bin/bash ./while.sh
root 2709 2612 0 10:45 pts/1 00:00:00 grep --color=auto while.sh
[root@bigData01 test]# kill 2700
[root@bigData01 test]# ps -ef | grep while.sh
root 2717 2612 0 10:45 pts/1 00:00:00 grep --color=auto while.sh
[1]+ Terminated while.sh
[root@bigData01 test]# nohup while.sh &
[1] 2727
[root@bigData01 test]# nohup: ignoring input and appending output to ‘nohup.out’
[root@bigData01 test]# ll
total 12
-rwxr--r--. 1 root root 199 Jan 16 21:53 hello.sh
-rw-r--r--. 1 root root 364 Jan 16 22:50 if.sh
-rw-------. 1 root root 0 Jan 17 10:45 nohup.out
-rwxr--r--. 1 root root 55 Jan 17 10:44 while.sh
[root@bigData01 test]# kill 2727
nohup
命令下在后台运行的脚本只能先使用ps -ef | grep <关键字>
查询pid,然后通过kill
命令去终止。
6. 标准输出、标准错误输出和重定向
标准输出:表示是命令或者程序输出的正常信息。
标准错误输出:表示是命令或者程序输出的错误信息。
重定向就是将标准输出导向一个文件或者追加到一个文件中。
其中,标准输出可以使用文件描述符1来表示,标准错误输出可以使用文件描述符2来表示。针对标准输出和标准错误输出,可以使用重定向操作将这些输出信息保存到文件中。
例如:
ll 1>a.txt
表示将ll
命令的标准输出重定向到a.txt文件中,其中>
在重定向时会覆盖文件原有内容,>>
表示会追加到原有内容之后。ll 1>a.txt
的1
可以省略,因为默认情况下不写也是1
。lk
是一个不存在的错误命令,lk 2>b.txt
表示会将lk
命令的标准错误输出重定向到b.txt中。
[root@bigData01 test]# ll 1>a.txt
[root@bigData01 test]# cat a.txt
total 12
-rw-r--r--. 1 root root 0 Jan 17 10:59 a.txt
-rwxr--r--. 1 root root 199 Jan 16 21:53 hello.sh
-rw-r--r--. 1 root root 364 Jan 16 22:50 if.sh
-rw-------. 1 root root 0 Jan 17 10:45 nohup.out
-rwxr--r--. 1 root root 55 Jan 17 10:44 while.sh
[root@bigData01 test]# ll 1>>a.txt
[root@bigData01 test]# cat a.txt
total 12
-rw-r--r--. 1 root root 0 Jan 17 10:59 a.txt
-rwxr--r--. 1 root root 199 Jan 16 21:53 hello.sh
-rw-r--r--. 1 root root 364 Jan 16 22:50 if.sh
-rw-------. 1 root root 0 Jan 17 10:45 nohup.out
-rwxr--r--. 1 root root 55 Jan 17 10:44 while.sh
total 16
-rw-r--r--. 1 root root 254 Jan 17 10:59 a.txt
-rwxr--r--. 1 root root 199 Jan 16 21:53 hello.sh
-rw-r--r--. 1 root root 364 Jan 16 22:50 if.sh
-rw-------. 1 root root 0 Jan 17 10:45 nohup.out
-rwxr--r--. 1 root root 55 Jan 17 10:44 while.sh
[root@bigData01 test]# lk
bash: lk: command not found...
[root@bigData01 test]# lk 2>b.txt
[root@bigData01 test]# cat b.txt
bash: lk: command not found...
最后再看一个综合案例:nohup hello.sh >/dev/null 2>&1 &
这条命令中各部分的含义:
nohup和&:可以让程序一直在后台运行
/dev/null:是linux中的黑洞,任何数据扔进去都找不到了
/dev/null:把标准输出重定向到黑洞中,表示脚本的输出信息不需要存储
2>&1 :表示是把标准错误输出重定向到标准输出中
因此这条命令的意思是把脚本放在后台一直运行,并且把所有输出都扔到黑洞里面。
7. 定时执行shell
- 在使用该服务之前,先检查
crontab
服务状态:
[root@bigData01 test]# systemctl status crond
crond.service - Command Scheduler
Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled)
Active: active (running) since Tue 2023-01-17 10:38:35 CST; 1h 37min ago
Main PID: 830 (crond)
CGroup: /system.slice/crond.service
└─830 /usr/sbin/crond -n
Jan 17 10:38:35 bigData01 systemd[1]: Started Command Scheduler.
Jan 17 10:38:36 bigData01 crond[830]: (CRON) INFO (RANDOM_DELAY will be scaled with factor 2% if used.)
Jan 17 10:38:37 bigData01 crond[830]: (CRON) INFO (running with inotify support)
看到里面的active(running)
说明这个服务是启动的,如果服务没有启动可以使用systemctl start crond
来启动,如果想要停止可以使用systemctl stop cron
。
- 查看当前定时任务
使用命令crontab -l
,如下:
[root@bigData01 test]# crontab -l
no crontab for root
- 设置定时任务:
设置定时任务时使用命令crontab -e
,随后会进入一个定时任务配置文件,在文件中按照格式输入命令即可。写入文件后保存退出即可,若要取消该定时任务则需进入文件将命令行删除。
crontab
的命令格式为:* * * * * <username> <command>
这五个*
分别表示分钟(0~59)、小时(0~23)、日期(1~31)、月份(1~12)、星期数(0~6),如果哪一项要求每个该时间点都要执行,那么就将该项数据置为*
,否则改为间隔时间数据。例如:
每小时的第3分钟执行一次a.sh脚本文件(文件地址最好写为绝对地址):7 * * * * sh /test/a.sh
每天3点执行一次a.sh脚本文件:0 3 * * * sh /test/a.sh
每天18点30分执行一次a.sh脚本文件:30 18 * * * sh /test/a.sh
每个月的1号的12点15分执行一次a.sh脚本文件:15 12 1 * * sh /test/a.sh
每年的3月1号的11点45分执行一次a.sh脚本文件:45 11 1 3 * sh /test/a.sh
每周三的16点30分执行一次a.sh脚本文件:30 16 * * 3 sh /test/a.sh