【Linux】shell基本语法

学习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.shsh ./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循环

  1. 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
  1. 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.txt1可以省略,因为默认情况下不写也是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

  1. 在使用该服务之前,先检查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

  1. 查看当前定时任务
    使用命令crontab -l,如下:
[root@bigData01 test]# crontab -l
no crontab for root
  1. 设置定时任务:
    设置定时任务时使用命令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

posted @ 2023-01-17 13:19  ccql  阅读(13)  评论(0编辑  收藏  举报  来源