一个计算机技术爱好者与学习者

0%

好好学Shell:Shell脚本编程入门篇(上)

1. Shell简介

Shell 是什么?
Shell 这个单词的原意是“外壳”,跟 kernel(内核)相对应,比喻内核外面的一层,即用户跟内核交互的对话界面。

具体来说,Shell 这个词有多种含义。

首先,Shell 是一个程序,提供一个与用户对话的环境。这个环境只有一个命令提示符,让用户从键盘输入命令,所以又称为命令行环境(command line interface,简写为 CLI)。Shell 接收到用户输入的命令,将命令送入操作系统执行,并将结果返回给用户。

其次,Shell 是一个命令解释器,解释用户输入的命令。它支持变量、条件判断、循环操作等语法,所以用户可以用 Shell 命令写出各种小程序,又称为脚本(script)。这些脚本都通过 Shell 的解释执行,而不通过编译。

最后,Shell 是一个工具箱,提供了各种小工具,供用户方便地使用操作系统的功能。

对于用户来说,Shell是最重要的实用程序,深入了解和熟练掌握Shell的特性极其使用方法,是用好Unix/Linux系统的关键。可以说,Shell使用的熟练程度反映了用户对Unix/Linux使用的熟练程度。

Shell 有两种执行命令的方式:
交互式(Interactive):解释执行用户的命令,用户输入一条命令,Shell就解释执行一条。
批处理(Batch):用户事先写一个Shell脚本,其中有很多条命令,让Shell一次把这些命令执行完。

Shell脚本是解释型语言,不需要编译。Shell程序从脚本中一行一行读取并执行这些命令,相当于一个用户把脚本中的命令一行一行敲到Shell提示符下执行。

Unix/Linux 上常见的 Shell 脚本解释器有 bash、sh、csh、ksh 等,习惯上把它们称作一种Shell。我们常说有多少种 Shell,其实说的是 Shell 脚本解释器。

Bash 是大多数 Linux 标准默认的 Shell,因此是我们学习的重点。

参考文档:

2. 体验Shell脚本

2.1. helloworld

1、新建helloworld.sh,内容为:

1
2
#!/bin/bash
echo "Hello World !"

#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shell。echo命令用于向窗口输出文本。

2、添加执行权限
chmod +x helloworld.sh

3、执行脚本
./helloworld.sh

PS:输入gg跳到首行,.,$d,表示从当前行到末行全部删除掉。或者,1,$d,表示从首行到末行全部删除掉。

2.2. echo

echo是Shell的一个内部指令,用于在屏幕上打印出指定的字符串。

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
#!/bin/bash

# 显示转义字符
echo "\"It is a test\""

# 显示变量
name="VoidKing"
echo "Welcome, $name"

# 变量与其它字符相连时需要使用大括号
month=8
echo "${month}-1-2009"

# 显示换行
echo -e "OK!\n"
echo "It is a test"

# 显示不换行
echo -e "OK!\c"
echo "It is a test"

# 显示结果重定向至文件
echo "It is a test" > myfile

# 原样输出字符串
echo '$name\"'

# 显示命令执行结果
echo `date`

2.3. printf

printf 命令用于格式化输出, 是echo命令的增强版。它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同。

注意:printf 由 POSIX 标准所定义,移植性要比 echo 好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

# format-string为双引号
printf "%d %s\n" 1 "abc"

# 单引号与双引号效果一样
printf '%d %s\n' 1 "abc"

# 没有引号也可以输出
printf %s abcdef

# 一个格式多个参数
printf %s abc def
printf "%s\n" abc def
printf "%s %s %s\n" a b c d e f g h i j

# %s默认为空,%d默认为0
printf "%s and %d \n"

# 如果以%d的格式来显示字符串,那么会有警告,并且默认置为 0
printf "always prints %s,%d\n" Hello Shell

2.4. 交互

使用 read 命令从 stdin 获取输入并赋值给 PERSON 变量,最后在 stdout 上输出。

1
2
3
4
5
#!/bin/bash

echo "What is your name?"
read PERSON
echo "Hello, $PERSON"

2.5. 重定向

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash

# stdout重定向到file
ls -l > file
ls -l >> file

# stdout和stderr合并后重定向到myfile
ls -l > myfile 2>&1
ls -l >> myfile 2>&1

# 屏蔽 stdout 和 stderr
ls -l > /dev/null 2>&1
1
2
3
4
5
6
#!/bin/bash

cat << EOF
My name is VoidKing.
I'm good at programming.
EOF

2.6. 包含脚本

url.sh内容为

1
url="http://www.voidking.com"

main.sh内容为

1
2
3
4
5
6
#!/bin/bash

. ./helloworld.sh
source ./helloworld.sh
. ./url.sh
echo $url

3. 变量

3.1. 基本变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

# 定义变量
your_name="voidking"
your_age=25

# 显示变量
echo -e "your name is ${your_name} \n welcome to shell world"
echo "your age is ${your_age}"

your_email="voidking@qq.com"

# 设置your_email为只读变量
readonly your_email

echo "your email is $your_email"

your_address="中国吉林长春"

# 删除变量
unset your_address

注意,变量名和等号之间不能有空格,这可能和我们熟悉的所有编程语言都不一样。
shell里没有多行注释,只能每一行加一个#号。

上面的脚本中,我们使用了一些中文。在CentOS7.2中,如果出现乱码,那么,我们需要添加CentOS7.2的中文支持。
locale,显示LANG=C。

vim /etc/locale.conf,修改locale.conf如下:

1
2
3
4
LANG="zh_CN.UTF-8"
LANGUAGE="zh_CN.UTF-8:zh_CN.UTF-8:zh_CN"
SUPPORTED="zh_CN.UTF-8:zh_CN:zh:en_US.UTF-8:en_US:en"
SYSFONT="lat0-sun16"

locale,显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
LANG=zh_CN.UTF-8
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=

3.2. 特殊变量

1
2
3
4
5
6
7
8
9
#!/bin/bash

echo "Process ID: $$"
echo "File Name: $0"
echo "First Parameter: $1"
echo "Second Parameter: $2"
echo "Quoted Values: $@"
echo "Quoted Values: $*"
echo "Total Number of Parameters: $#"

3.3. 变量替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

echo ${var:-"Variable is not set"}
echo "1 - Value of var is ${var}"

echo ${var:="Variable is not set"}
echo "2 - Value of var is ${var}"

unset var
echo ${var:+"This is default value"}
echo "3 - Value of var is $var"

var="Prefix"
echo ${var:+"This is default value"}
echo "4 - Value of var is $var"

echo ${var:?"Print this message"}
echo "5 - Value of var is ${var}"

3.4. 命令替换

1
2
3
4
5
6
7
8
#!/bin/bash

DATE=`date`
echo "Date is $DATE"
USERS=`who | awk '{print $1}'`
echo "Logged in user are $USERS"
UP=`date ; uptime`
echo "Uptime is $UP"

3.5. 字符串

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
#!/bin/bash

# 单引号字符串中的变量无效,任何字符都会原样输出
# 单引号字串中不能出现单引号(对单引号使用转义符后也不行)
str='this is a string'

# 双引号里可以有变量,可以出现转义符
str="Hello, I know your are \"$your_name\"! \n"


# 拼接字符串
your_name="voidking"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1

# 获取字符串长度
string="abcd"
echo ${#string}

# 提取子字符串
string="alibaba is a great company"
echo ${string:1:4}

# 查找子字符串
string="alibaba is a great company"
echo `expr index "$string" is`

3.6. 数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/sh

name[0]="VoidKing0"
name[1]="VoidKing1"
name[2]="VoidKing2"
echo "Index0: ${name[0]}"
echo "Index1: ${name[1]}"

echo "All names: ${name[*]}"
echo "All names: ${name[@]}"

age=(24 25 26)
age[2]=25

# 取得数组元素的个数
name_length=${#name[@]}
age_length=${#age[*]}
echo "name_length: $name_length,age_length: $age_length"

# 取得数组单个元素的长度
lengthn=${#name[0]}
echo "Index0 length: $lengthn"

4. 源码分享

https://github.com/voidking/shell.git