Shell基础
Shell脚本文件第一行 #!/bin/bash
,用于告诉系统解释此脚本文件的Shell程序。常见的Shell程序有Bourne Shell(/bin/sh
)和Bourne Again Shell(/bin/bash
)。
脚本文件的扩展名不影响脚本执行。
运行 Shell 脚本有两种方法:
- 作为可执行程序
chmod +x ./test.sh #使脚本具有执行权限
./test.sh #执行脚本
注意,一定要写成 ./test.sh
,告诉系统在当前目录找该文件,否则系统会去PATH
里寻找test.sh
,而只有/bin
, /sbin
, /usr/bin
,/usr/sbin
等会在PATH
里。
- 作为解释器参数
/bin/sh test.sh
此时,文件中的第一行指定的解释器信息是无效的。
双击tab
键可补全命令
history
可以查看命令历史
使用env
命令可列出系统预设的全部环境变量,通常预设的变量都是大写的,如PATH
。
通过echo $PATH
可显示变量的值。
常见的环境变量有:
PATH
决定了shell将到哪些目录中寻找命令或程序
HOME
当前用户主目录
HISTSIZE
历史记录数
LOGNAME
当前用户的登录名
HOSTNAME
指主机的名称
SHELL
前用户Shell类型
LANG
语言相关的环境变量,多语言可以修改此环境变量
PWD
当前目录
使用set
命令可列出系统预设的全部变量及用户自定义变量。
设定变量的格式为a=b
,其中a为变量名,b为变量的内容,等号两边不能有空格;
想取消某个变量,只要输入unset 变量名
即可。
需要注意的是,用户自定义变量只在当前shell生效。
dylan@DESKTOP-FRA3DA2:~$ myvar=MyVar
dylan@DESKTOP-FRA3DA2:~$ echo $myvar
MyVar
dylan@DESKTOP-FRA3DA2:~$ bash #通过bash命令打开一个新的bash
dylan@DESKTOP-FRA3DA2:~$ echo $myvar
dylan@DESKTOP-FRA3DA2:~$ exit #退出当前shell,回到旧的shell
exit
dylan@DESKTOP-FRA3DA2:~$ echo $myvar
MyVar
dylan@DESKTOP-FRA3DA2:~$
如果要永久生效,依据生效的范围不同,如下:
- 要想系统内所有用户登录后都能使用该变量
需要在/etc/profile
文件最末行加入export myvar=MyVar
然后运行source /etc/profile
就可以生效了。 - 只想让当前用户使用该变量
需要在用户主目录下的.bashrc
文件最后一行加入export myvar=MyVar
然后运行source .bashrc
就可以生效了。
如果在当前shell中运行bash指令后,则会进入一个新的shell,这个shell就是原来shell的子shell了,可用pstree
指令来查看。pstree
这个指令会把linux系统中所有进程通过树形结构打印出来。
dylan@DESKTOP-FRA3DA2:~$ pstree
init─┬─init───bash───su───bash───su───bash───pstree
└─{init}
在父shell中设定一个变量后,进入子shell后该变量是不会生效的,如果想让这个变量在子shell中生效则要用到export
指令:
dylan@DESKTOP-FRA3DA2:~$ myvar=MyVar
dylan@DESKTOP-FRA3DA2:~$ echo $myvar
MyVar
dylan@DESKTOP-FRA3DA2:~$ bash
dylan@DESKTOP-FRA3DA2:~$ echo $myvar
dylan@DESKTOP-FRA3DA2:~$ exit
exit
dylan@DESKTOP-FRA3DA2:~$ export myvar
dylan@DESKTOP-FRA3DA2:~$ bash
dylan@DESKTOP-FRA3DA2:~$ echo $myvar
MyVar
几个重要文件:
系统级别:
/etc/profile
:这个文件预设了几个重要的变量,例如PATH
, USER
, LOGNAME
, MAIL
, INPUTRC
, HOSTNAME
, HISTSIZE
, umas
等等。
/etc/bashrc
:这个文件主要预设umask
以及PS1
。PS1
是指命令提示符,如 dylan@DESKTOP-FRA3DA2:~$
。
用户级别,在用户目录下:
.bash_profile
:定义了用户的个人化路径与环境变量的文件名称。每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次。
.bashrc
:该文件包含专用于你的shell的bash信息,当登录时以及每次打开新的shell时,该该文件被读取。例如你可以将用户自定义的alias或者自定义变量写到这个文件中。
.bash_history
:记录命令历史用的。
.bash_logout
:当退出shell时,会执行该文件。可以把一些清理的工作放到这个文件中。
常用命令:
- cut 截取某一个字段
语法:cut -d “分隔字符” [-cf] n #这里的n是数字
-d:后面跟分隔字符,分隔字符要用双引号括起来
-c:后面接的是第几个字符
-f:后面接的是第几个区块
root@DESKTOP-FRA3DA2:~# cat /etc/passwd | cut -d ":" -f 1
root
daemon
bin
...
root@DESKTOP-FRA3DA2:~# cat /etc/passwd | cut -d ":" -f 2
x
x
x
...
root@DESKTOP-FRA3DA2:~# cat /etc/passwd | cut -c 1
r
d
b
...
root@DESKTOP-FRA3DA2:~# cat /etc/passwd | cut -c 1,4
rt
dm
b:
...
root@DESKTOP-FRA3DA2:~# cat /etc/passwd | cut -c 1-4
root
daem
bin:
...
- sort 排序
语法:sort [-t 分隔符] [-kn1,n2] [-nru],这里的n1 < n2
-t 分隔符
-n:使用纯数字排序
-r:反向排序
-u:去重复
-kn1,n2 :由n1区间排序到n2区间,可以只写-kn1,即对n1字段排序
root@DESKTOP-FRA3DA2:~# cat /etc/passwd | sort
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...
root@DESKTOP-FRA3DA2:~# cat /etc/passwd | sort -t: -k3n
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...
root@DESKTOP-FRA3DA2:~# cat /etc/passwd | sort -t: -k3nr
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
una:x:1002:1002::/home/una:/bin/bash
chan:x:1001:1001::/home/chan:/bin/bash
...
root@DESKTOP-FRA3DA2:~# cat /etc/passwd | sort -t: -k3,5n
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...
- wc 统计文档的行数、字符数、词数
-l:统计行数
-m:统计字符数
-w:统计词数
dylan@DESKTOP-FRA3DA2:~/test$ echo "hello world" > test.txt
dylan@DESKTOP-FRA3DA2:~/test$ echo "hello world again" >> test.txt
dylan@DESKTOP-FRA3DA2:~/test$ cat test.txt | wc -l
2
dylan@DESKTOP-FRA3DA2:~/test$ cat test.txt | wc -w
5
dylan@DESKTOP-FRA3DA2:~/test$ cat test.txt | wc -m
30
- uniq 去重复的行
-c:统计重复的行数,并把行数写在前面
dylan@DESKTOP-FRA3DA2:~/test$ cat test.txt
hello world
hello world again
hello world again
dylan@DESKTOP-FRA3DA2:~/test$ cat test.txt | sort | uniq
hello world
hello world again
dylan@DESKTOP-FRA3DA2:~/test$ cat test.txt | sort | uniq -c
1 hello world
2 hello world again
注意:在进行uniq
之前,需要先用sort
排序然后才能uniq
- tee 后跟文件名,类似与重定向">",但是比重定向多了一个功能,在把文件写入后面所跟的文件中的同时,还显示在屏幕上。
dylan@DESKTOP-FRA3DA2:~/test$ echo "hello world again" | tee test.txt
hello world again
dylan@DESKTOP-FRA3DA2:~/test$ cat test.txt
hello world again
- split 切割文档
-b :依据大小来分割文档,单位为byte
-l :依据行数来分割文档
dylan@DESKTOP-FRA3DA2:~/test$ ll
total 0
drwxr-xr-x 1 dylan dylan 512 Sep 23 14:44 ./
drwxr-xr-x 1 dylan dylan 512 Sep 23 10:54 ../
-rw-rw-r-- 1 dylan dylan 90 Sep 23 14:42 test.txt
-rw-r--r-- 1 dylan dylan 0 Sep 22 19:25 test_file.txt
-rwxr-xr-x 1 dylan dylan 129 Sep 22 11:36 test_shell.bash*
dylan@DESKTOP-FRA3DA2:~/test$ split -b 30 test.txt test
dylan@DESKTOP-FRA3DA2:~/test$ ll
total 0
drwxr-xr-x 1 dylan dylan 512 Sep 23 14:44 ./
drwxr-xr-x 1 dylan dylan 512 Sep 23 10:54 ../
-rw-rw-r-- 1 dylan dylan 90 Sep 23 14:42 test.txt
-rw-r--r-- 1 dylan dylan 0 Sep 22 19:25 test_file.txt
-rwxr-xr-x 1 dylan dylan 129 Sep 22 11:36 test_shell.bash*
-rw-rw-r-- 1 dylan dylan 30 Sep 23 14:44 testaa
-rw-rw-r-- 1 dylan dylan 30 Sep 23 14:44 testab
-rw-rw-r-- 1 dylan dylan 30 Sep 23 14:44 testac
dylan@DESKTOP-FRA3DA2:~/test$ cat testaa
hello world again
hello world dylan@DESKTOP-FRA3DA2:~/test$
dylan@DESKTOP-FRA3DA2:~/test$ cat test.txt
hello world again 1
hello world again 2
hello world again 3
hello world again 4
hello world again 5
dylan@DESKTOP-FRA3DA2:~/test$ split -l 3 test.txt testl
dylan@DESKTOP-FRA3DA2:~/test$ ll
total 0
drwxr-xr-x 1 dylan dylan 512 Sep 23 14:48 ./
drwxr-xr-x 1 dylan dylan 512 Sep 23 14:48 ../
-rw-rw-r-- 1 dylan dylan 100 Sep 23 14:48 test.txt
-rw-r--r-- 1 dylan dylan 0 Sep 22 19:25 test_file.txt
-rwxr-xr-x 1 dylan dylan 129 Sep 22 11:36 test_shell.bash*
-rw-rw-r-- 1 dylan dylan 30 Sep 23 14:44 testaa
-rw-rw-r-- 1 dylan dylan 30 Sep 23 14:44 testab
-rw-rw-r-- 1 dylan dylan 30 Sep 23 14:44 testac
-rw-rw-r-- 1 dylan dylan 60 Sep 23 14:48 testlaa
-rw-rw-r-- 1 dylan dylan 40 Sep 23 14:48 testlab
dylan@DESKTOP-FRA3DA2:~/test$ cat testlaa
hello world again 1
hello world again 2
hello world again 3
- grep 查找
语法:grep [-cinvABC] ‘word’ filename
-c:打印符合要求的行数
-i:忽略大小写
-n:在输出符合要求的行的同时连同行号一起输出
-v:打印不符合要求的行
-A:后跟一个数字(有无空格都可以),例如 –A2则表示打印符合要求的行以及下面两行
-B:后跟一个数字,例如 –B2 则表示打印符合要求的行以及上面两行
-C:后跟一个数字,例如 –C2 则表示打印符合要求的行以及上下各两行
egrep
是grep
的扩展版,支持’+
’(匹配1次或多次),’?
’(匹配0次或1次),’|
’,’()
’(分组)这几个规则
Shell编程基础
1. Shell变量
- 定义变量如下:
my_var="abc"
需要注意的是,变量名与等号之间不能有空格;变量的命名只能使用字母、数字及下划线,且首个字符不能为数字;不能使用空格、标点符号及bash中的关键字。
使用变量:
echo ${my_var}
注意,需要在变量名前加"$
",另外,花括号是可选的,用于某些情况用于区分边界,良好的习惯是加上{}。
- 只读变量:
通过readonly
命令可以将变量定义为只读变量,该变量的值不能改变,例如:
readonly my_var
- 删除变量:
使用unset
命令可以将变量删除,例如:
unset my_var
注意,unset
命令不能删除只读变量
- 变量类型:
- 局部变量 在脚本中定义且只在当前实例中生效
- 环境变量 所有的程序,包括Shell启动的程序,都能访问环境变量
- Shell变量 Shell变量是由Shell程序设置的特殊变量
2. 字符串:
可用单引号、双引号或者不适用引号。
单引号与双引号的区别:
单引号中不能出现单独的一个单引号(’),即使转义也不行;单引号中的变量智慧原样输出而不能转化为变量值输出。需要注意,var1='hello, '$my_var'!'
中的my_var
是可以转化为变量值的,但var2='hello, ${my_var}!'
则不行。
双引号中可以出现转义字符;双引号中的变量可以转化成变量值。
- 字符串拼接:
通过单引号拼接:
var1='hello, '$my_var'!'
var2='hello, ${my_var}!'
通过双引号拼接:
var3="hello, "$my_var"!"
var4="hello, ${my_var}!"
- 字符串的长度:
echo ${#my_var} #输出3
- 字符串提取子字符串:
echo ${#my_var:1:2} #输出bc,表示从第2个字符开始截取2个字符
- 查找子字符串:
string="this is for test"
echo `expr index "$string" io` #输出3,此处表示查找i或o的位置,哪个先出现就计算哪个的
3. Shell数组:
注意,bash仅支持一维数组,数组下标从0开始,初始化时不需要定义数组大小。
- 数组赋值:
1 array_name=(v1 v2 v3)或
array_name=(
v1
v2
v3
)
2 array_name[0]=v1
array_name[1]=v2
array_name[2]=v3
- 数组读取:
1 ${array_name[n]}
2 ${array_name[@]} # 获取所有元素
- 数组长度:
1 ${#array_name[@]} # 取得数组元素的个数
2 ${#array_name[*]} # 取得数组元素的个数
3 ${#array_name[n]} # 取得数组下标为n的元素的长度
4. 注释:
- 单行注释,以
#
开头 - 多行注释,如下:
:<<EOF
注释内容...
EOF
5. Shell传递参数:
在执行Shell脚本时可以传参数,在脚本中用$n
获取,$0
表示当前脚本文件的名称,$1
表示第1个参数,以此类推。
另外,
$#
,表示传递的参数的个数;
$*
,以一个字符串接受所有参数,如果是"$*
",则以"$1 $2 ..."
形式输出;
$@
,同上,不同的是,如果是"$@
",则以"$1" "$2"
形式输出;
$$
,表示脚本运行的当前进程ID号;
$!
,表示后台运行的最后一个进程的ID号;
$-
,显示Shell使用的当前选项,与set命令功能相同。
$?
,显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
6. Shell运算符:
- 数学运算
原生bash不支持简单的数学运算,可通过expr
表达式计算工具来实现,如下:
val1=`expr $a + $b`
val2=`expr $a - $b`
val3=`expr $a \* $b`
val4=`expr $a / $b`
val5=`expr $a % $b`
注意:表达式和运算符之间要有空格;完整的表达式要被 `` 包含;乘法"*
“需要转义符号”\
"。
- 关系运算:
==
用于比较两个数字是否相等,相同则返回true。[ $a == $b ]
返回false
!=
用于比较两个数字是否不相等,不相同则返回true。[ $a != $b ]
返回true
-eq
检测两个数是否相等,相等返回true。[ $a -eq $b ]
-ne
检测两个数是否不相等,不相等返回true。[ $a -ne $b ]
-gt
检测左边的数是否大于右边的,如果是,则返回true。[ $a -gt $b ]
-lt
检测左边的数是否小于右边的,如果是,则返回true。[ $a -lt $b ]
-ge
检测左边的数是否大于等于右边的,如果是,则返回true。[ $a -ge $b ]
-le
检测左边的数是否小于等于右边的,如果是,则返回true。[ $a -le $b ]
注意:条件表达式要放在方括号之间,并且要有空格。 - 布尔运算:
!
非运算,表达式为true则返回false,否则返回true。[ ! false ]
-o
或运算,有一个表达式为true则返回true。[ $a -lt 20 -o $b -gt 100 ]
-a
与运算,两个表达式都为true才返回true。[ $a -lt 20 -a $b -gt 100 ]
- 逻辑运算符:
&&
逻辑的AND,[[ $a -lt 100 && $b -gt 100 ]]
||
逻辑的OR,[[ $a -lt 100 || $b -gt 100 ]]
- 字符串运算符:
=
检测两个字符串是否相等,相等返回true。[ $a = $b ]
!=
检测两个字符串是否相等,不相等返回true。[ $a != $b ]
-z
检测字符串长度是否为0,为0返回true。[ -z $a ]
-n
检测字符串长度是否不为 0,不为0返回true。[ -n $a ]
$
检测字符串是否为空,不为空返回true。[ $a ]
实例:
#!/bin/bash
a=10
b=20
# 关系运算
if [ $a -eq $b ]
then
echo "$a -eq $b : a 等于 b"
else
echo "$a -eq $b: a 不等于 b"
fi
# 布尔运算
if [ $a -lt 100 -a $b -gt 15 ]
then
echo "$a 小于 100 且 $b 大于 15 : 返回 true"
else
echo "$a 小于 100 且 $b 大于 15 : 返回 false"
fi
# 逻辑运算
if [[ $a -lt 100 && $b -gt 100 ]]
then
echo "返回 true"
else
echo "返回 false"
fi
7. Shell echo命令:
用于字符串的输出,输出字符串时,双引号可加可不加;
可以显示转义字符;
可以显示变量;原样输出变量时,用单引号;
可显示换行(\n)与不换行(echo -e "\c"
, -e 开启转义 \c 不换行);
可将显示结果定向至文件;
可显示命令执行结果,echo
date(显示当前日期)。
8. read可用于接受标准输入,read var
将标准输入赋值给var。
9. printf
默认printf
不会像echo
自动添加换行符,我们可以手动添加\n
。
printf命令的语法:
printf format-string [arguments...]
其中,format-string如下:
%s
%c
%d
%f
都是格式替代符。
%-10s
指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
%-4.2f
指格式化为小数,其中.2指保留2位小数,4同上表示字符宽度。
更多实例:
# format-string为双引号
printf "%d %s\n" 1 "abc"
# 单引号与双引号效果一样
printf '%d %s\n' 1 "abc"
# 没有引号也可以输出
printf %s abcdef
# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def
printf "%s\n" abc def
printf "%s %s %s\n" a b c d e f g h i j
# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"
输出结果:
1 abc
1 abc
abcdefabcdefabc
def
a b c
d e f
g h i
j
and 0
10. test
test
命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
数值测试:
num1=100
num2=100
if test $[num1] -eq $[num2]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi
其中,
-eq | 等于则为真 |
-ne | 不等于则为真 |
-gt | 大于则为真 |
-ge | 大于等于则为真 |
-lt | 小于则为真 |
-le | 小于等于则为真 |
注意,$[num1]
中的[]执行基本的算数运算,即此处num1可替换成$[50+50]
,注意等号两边不能有空格
字符串测试:
num1="ru1noob"
num2="runoob"
if test $num1 = $num2
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi
其中,
= | 等于则为真 |
!= | 不相等则为真 |
-z 字符串 | 字符串的长度为零则为真 |
-n 字符串 | 字符串的长度不为零则为真 |
文件测试:
if test -e ./bash
then
echo '文件已存在!'
else
echo '文件不存在!'
fi
其中,
-e 文件名 | 如果文件存在则为真 |
-r 文件名 | 如果文件存在且可读则为真 |
-w 文件名 | 如果文件存在且可写则为真 |
-x 文件名 | 如果文件存在且可执行则为真 |
-s 文件名 | 如果文件存在且至少有一个字符则为真 |
-d 文件名 | 如果文件存在且为目录则为真 |
-f 文件名 | 如果文件存在且为普通文件则为真 |
-c 文件名 | 如果文件存在且为字符型特殊文件则为真 |
-b 文件名 | 如果文件存在且为块特殊文件则为真 |
另外,可用与(-a
)、或(-o
)、非(!
)三个逻辑操作符用于将测试条件连接起来,其优先级为:!
最高,-a
次之,-o
最低。
如下:
if test -e ./notFile -o -e ./bash
then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi
11. 流程控制:
- if语句:
if condition
then
command1
command2
...
commandN
else
command
fi
- for循环:
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
- while语句:
while condition
do
command
done
- util循环:
until condition
do
command
done
- case:
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
取值后面必须为单词in
,取值可以为变量或常数。
每一模式必须以右括号结束,模式支持正则表达式。
匹配发现取值符合某一模式后,其间所有命令开始执行直至";;
",";;
"表示 break。
12. Shell函数:
- 函数定义
[function] funname() {
var=100
do sth
return $var
}
function可省略,可用return
显式返回结果,否则则返回最后一条命令的结果。
通过函数名funname
即可调用函数,函数返回值再调用函数后通过$?
来获取。
在Shell中,调用函数时可以向其传递参数。在函数体内部,通过$n
的形式来获取参数的值,例如,$1
表示第一个参数,$2
表示第二个参数,当n>=10时,需要使用${n}
来获取参数。此处同“Shell传递参数”。
13. 重定向:
一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
标准输入文件(stdin):stdin
的文件描述符为0,Unix程序默认从stdin
读取数据。
标准输出文件(stdout):stdout
的文件描述符为1,Unix程序默认向stdout
输出数据。
标准错误文件(stderr):stderr
的文件描述符为2,Unix程序会向stderr
流中写入错误信息。
默认情况下,command > file
将stdout
重定向到file
,command < file
将stdin
重定向到file。
如果希望 stderr
重定向到 file
,可以这样写:
$ command 2 > file
如果希望 stderr
追加到 file
文件末尾,可以这样写:
$ command 2 >> file
2 表示标准错误文件(stderr)。
如果希望将 stdout
和 stderr
合并后重定向到 file
,可以这样写:
$ command > file 2>&1
或者
$ command >> file 2>&1
如果希望对 stdin
和 stdout
都重定向,可以这样写:
$ command < file1 > file2
command
命令将 stdin
重定向到 file1
,将 stdout
重定向到 file2
。
Here Document是Shell中的一种特殊的重定向方式,用来将输入重定向到一个交互式Shell脚本或程序。
它的基本的形式如下:
command << delimiter
document
delimiter
它的作用是将两个delimiter
(分隔符)之间的内容(document) 作为输入传递给command
。
注意:
结尾的delimiter
一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和tab缩进。
开始的delimiter
前后的空格会被忽略掉。
$ wc -l << EOF
111
222
333
EOF
3 # 输出结果为 3 行
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到/dev/null
。
/dev/null
是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。
如果希望屏蔽 stdout
和 stderr
,可以这样写:
$ command > /dev/null 2>&1
14. 文件包含:
Shell 文件包含的语法格式如下:
. filename # 注意点号(.)和文件名中间有一空格
或
source filename
注:被包含的文件filename不需要可执行权限。