1、确定用户用什么shell
- echo $SHELL 查看环境变量
- 看passwd文件用户的启动命令 passwd文件路径/etc/passwd
- 修改用户的shell
- chsh
2、shell的内建命令
shell的内建命令是不会启动一个进程,而是就在当前shell的进程下去调用一个普通函数。shell的内建命令可以通过help去查询
3、编写一个简单的shell脚本test.sh
#!/bin/bash
echo HelloWorld
cd ..
ls
shell脚本中用#表示注释,相当于c语言的//注释。但如果#位于第一行开头,并且是#!则例外,它表示该脚本使用后面指定的解释器/bin/bash解释执行。如果把这个脚本文件加上可执行权限然后执行
[root@node02 shell_study]# chmod a+x test.sh
[root@node02 shell_study]# ./test.sh
4、变量
声明即赋值
varname=value
注意等号两边不能留空格,留了空格就变成了一个命令+两个参数
使用变量
$varname
${varname}
使用花括号来限定变量名的范围
5、变量的分类
shell 内变量
在shell的解析环境中存在的变量
全局范围的变量(全局变量)
shell中不使用任何修饰符修饰的变量都是全局变量
不管是在函数内还是函数外都一样
从声明语句调用开始一直到脚本结束 都是其声明周期
局部变量
用local 修饰
只能声明在函数内
从声明语句调用开始一直到函数结束
环境变量
是操作系统自带的,每一个进程都会有
当启动一个子进程的时候,环境变量是从父进程拷贝到子进程
子进程做任何环境变量的修改,不会影响父进程
环境变量是单向传递
export varname=value
或者
varname=value
export varname
删除变量
不管是shell内变量还是环境变量,使用unset删除变量
格式:unset 变量名
示例:
global.sh
#!/bin/bash
globalVar1="hello"
function test()
{
globalVar2="World"
local localVar3="玉米"
echo $localVar3
}
#调用函数
test
echo $globalVar1 $globalVar2 $localVar3
export environVar="this is in environ"
#调用子脚本
./subScript.sh
#验证子进程修改环境变量之后父进程有没有改变
echo $environVar
#删除环境变量
unset globalVar1
echo "globalVar1=" $globalVar1
子脚本subScript.sh
#!/bin/bash
echo "sub script start"
echo $globalVar1
echo "sub script end"
export environVar
echo $environVar
#更改环境变量
export environVar="SSSSSSSS"
6、文件名代换
代换发生在命令执行之前
* 匹配0个或多个任意字符
?匹配一个任意字符
[若干字符] 匹配方括号中任意一个字符的一次出现
代换成功的前提,文件必须存在,如果不存在是代换不了的
7、命令代换
执行某一条命令,将这个命令的标准输出的内容存到某个变量
varname=`cmd arg1 arg2..... `
$(cmd) 跟`cmd` 是一样的
获取当前脚本所在目录
curPath=$(cd `dirname $0`;pwd)
示例:
#!/bin/bash
dateTime=$(date)
#dateTime=`date`
echo "dateTime is " $dateTime
#获取当前脚本所在路径,在这个路径创建一个1.txt
curPath=$(cd `dirname $0`;pwd)
echo $curPath
touch $curPath/1.txt
8、算数代换
使用 $(()),只能用±*/,并且只能用于整数运算
var=45
echo $((var+3))
使用$[]也可以同样进行上述运算
var=88
echo $[var+90]
$[base#n], 其中base表示进制, n按照base进制解释,后面再有运算数,按十进制解释
echo $[8#12 + 2]
12
9、转义
将普通字符转换成特殊字符:\r \n
将特殊字符转换成普通字符:$SHELL
10、引号
单引号
保持字符串的字面值
即使字符串中有特殊符号也会转为普通符号
双引号
跟单引号一样,区别在于,支持变量的扩展
作为一个有经验的程序员,使用变量之前,如果变量是作为一个参数来传递的,应该要习惯性的加双引号,防止变量中有空格
示例:
[root@node02 shell_study]# vat=200
[root@node02 shell_study]# echo $vat
200
[root@node02 shell_study]# echo '$vat'
$vat
[root@node02 shell_study]# echo "$vat"
200
11、shell中如何表示真假
直接使用某条命令的返回状态来表示真假
main 函数的返回值
main函数返回 0 表示真
main函数返回非0 表示假
通过$? 来获取上一条命令的返回状态
[root@node02 shell_study]# $?
bash: 0: 未找到命令...
[root@node02 shell_study]# echo $?
127
[root@node02 shell_study]# echo 111
111
[root@node02 shell_study]# echo $?
0
12、条件测试
test
[ EXPRESSION ]
( EXPRESSION )
EXPRESSION is true
! EXPRESSION
EXPRESSION is false
EXPRESSION1 -a EXPRESSION2
both EXPRESSION1 and EXPRESSION2 are true
EXPRESSION1 -o EXPRESSION2
either EXPRESSION1 or EXPRESSION2 is true
-n STRING
the length of STRING is nonzero
STRING equivalent to -n STRING
-z STRING
the length of STRING is zero
STRING1 = STRING2
the strings are equal
STRING1 != STRING2
the strings are not equal
INTEGER1 -eq INTEGER2
INTEGER1 is equal to INTEGER2
INTEGER1 -ge INTEGER2
INTEGER1 is greater than or equal to INTEGER2
INTEGER1 -gt INTEGER2
INTEGER1 is greater than INTEGER2
INTEGER1 -le INTEGER2
INTEGER1 is less than or equal to INTEGER2
INTEGER1 -lt INTEGER2
INTEGER1 is less than INTEGER2
INTEGER1 -ne INTEGER2
INTEGER1 is not equal to INTEGER2
FILE1 -ef FILE2
FILE1 and FILE2 have the same device and inode numbers
FILE1 -nt FILE2
FILE1 is newer (modification date) than FILE2
FILE1 -ot FILE2
FILE1 is older than FILE2
-b FILE
FILE exists and is block special
-c FILE
FILE exists and is character special
-d FILE
FILE exists and is a directory
-e FILE
FILE exists
-f FILE
FILE exists and is a regular file
-g FILE
FILE exists and is set-group-ID
-G FILE
FILE exists and is owned by the effective group ID
-h FILE
FILE exists and is a symbolic link (same as -L)
-k FILE
FILE exists and has its sticky bit set
-L FILE
FILE exists and is a symbolic link (same as -h)
-O FILE
FILE exists and is owned by the effective user ID
-p FILE
FILE exists and is a named pipe
-r FILE
FILE exists and read permission is granted
-s FILE
FILE exists and has a size greater than zero
-S FILE
FILE exists and is a socket
-t FD file descriptor FD is opened on a terminal
-u FILE
FILE exists and its set-user-ID bit is set
-w FILE
FILE exists and write permission is granted
-x FILE
FILE exists and execute (or search) permission is granted
示例
[root@node02 shell_study]# mkdir test
[root@node02 shell_study]# test -d test //测试test是否为一个目录
[root@node02 shell_study]# echo $?
0 //是的
[root@node02 shell_study]# test -f test //测试test是否为一个文件
您在 /var/spool/mail/root 中有新邮件
[root@node02 shell_study]# echo $?
1 //不是
13、分支结构if
if 命令 | 条件测试
then
xxxx
elif 命令2 | 条件测试2 ;then #如果then跟if写在同一行,加分号
xxxx
else #else不加then
xxxx
fi
使用read读取用户输入
示例:
#!/bin/bash
echo "Is it morning? Please yes or no"
#读取用户输入的一个变量
read YES_OR_NO
if [ "$YES_OR_NO" = "yes" ]
then
echo "Good morning"
elif [ "$YES_OR_NO" = "no" ] ; then
echo "Good afternoon"
else
echo "Not recognized"
fi
执行结果
#!/bin/bash
if [ -f /bin/bash ]
then
echo "/bin/bash is a file"
else
echo "/bin/bash is not a file"
fi
var=1
var2=2
if [ "$var" -eq 1 ] && [ "$var2" -eq 2 ]
then
echo "yes"
else
echo "no"
fi
执行结果
简单的分支结构判断和执行使用 && 和 ||
make && sudo make install
如果make执行失败,那么是不会走后面的命令
echo xxxx || exit -1
如果前面的命令执行失败,那么就执行后面的命令
14、分支结构case
c语言的switch
switch(表达式)
{
case 值1:
xxxx
break;
case 值2:
xxxx
break;
default:
xxxx
break;
}
shell中的case
case 表达式 in
val1 | pattern1)
xxxx
;;
val2 | pattern2)
xxxx
;;
*) #相当于c语言中的default
xxxx
;; #相当于c语言中的break
esac #将case倒着写
示例:
#!/bin/bash
echo "Is it morning? Please answer yes or no!"
#读取用户输入的一个变量
read YES_OR_NO
case "$YES_OR_NO" in
[yY][eE][sS])
echo "Good morning"
;;
[nN][oO])
echo "Good afternoon"
;;
*)
echo "Not recognized"
;;
esac
15、批次创建文件/目录
touch {1..9}.txt 创建1~9.txt
mkdir {1..9}_dir 创建1~9_dir目录
16、for循环
格式:
for varname in 列表 ; do #do可以单独写一行,如果写在for这一行就要分号
…
echo $varname
done
控制循环次数
for i in {1…100}
do
…
done
遍历目录
for i in $(ls)
do
…
done
示例:
#!/bin/bash
for FRUIT in apple,banan,orangle
do
echo "I like $FRUIT"
done
#1-100累加
sum=0
for i in {1..100}
do
sum=$[$sum+$i]
done
echo "$sum"
# 遍历当前目录 是文件就输出xx是一个文件,是目录就输出xx是一个目录
for f in $(ls)
do
if [ -f "$f" ]
then
echo "$f is a regular file"
elif [ -d "$f" ]
then
echo "$f is a directory"
else
echo "$f is not recognized"
fi
done
17、while循环
和c语言差不多
格式:
while [ 条件测试 ] ; do
xxxx
done
示例:密码不对重新输入
#!/bin/bash
echo "Please input your passward"
read PASSWAD
while [ "$PASSWAD" != "123456" ]
do
echo "Sorry,try agin"
read PASSWAD
done
示例2:密码不对重新输入,输入次数不能大于5次
#!/bin/bash
echo "Please input your passward"
read PASSWAD
erroCount=1 #错误次数
while [ "$PASSWAD" != "123456" ]
do
if [ "$erroCount" -le 5 ]
then
echo "Sorry, try agin"
read PASSWAD
erroCount=$[$erroCount+1]
else
echo "Error than 5 times,exit"
break
fi
done
18、位置参数以及shift
$0 相当于c语言main函数的argv[0]
$1、$2… 这些称为位置参数,相当于c语言main函数的argv[1],argv[2],…
$# 相当于c语言main函数的argc-1,注意这里的#后面不表示注释
$@ 表示参数列表"$1" “$2”…,例如可以用在for循环中的in后面
$* 表示参数列表"$1" “$2” …,同上
$? 上一条命令的Exit Status
$(两个) 当前进程号
位置参数默认就支持10个,当然$@还是支持n个
可以配合shift来进行参数左移,来操作不定参数
示例:算数加法
#!/bin/bash
sum=0
#使用for循环
#for i in $@
#do
# sum=$[$sum+$i]
#done
#echo $sum
# 使用shift
while [ -n "$1" ]
do
sum=$[$sum+$1]
shift
done
echo $sum
执行结果
19、输出
echo -n 表示不换行
echo -e 解析转义字符
printf 跟c的printf一样
示例
20、管道
使用 | 将多个命令拼接在一起
原理,就是将前一个命令的标准输出作为后一个命令的标准输入来重定向,标准错误输出是不会重定向
需求,编写一个简单的管道命令,读取标准输入,将标准输入的数据转为大写,然后输出
upper.c
#include <stdio.h>
#include <ctype.h>
int main()
{
int ch = getchar();
while(ch != EOF)
{
putchar(toupper(ch));
ch = getchar();
}
return 0;
}
more 命令
将标准输入饿内容进行缓慢向下查看,要人工操作向下
只支持向下走,不支持往回走
less 命令
比more更加完善,支持回滚,也支持类似vim的操作,查找 hjkl等
21、tee命令
将标准输出重新输出,同时保存一份到文件
常用场景:开一个服务,服务一直在刷log,需要实时看到log,但是又想将log存成一个文件
示例:
22、函数
function 函数名() #小括号里面不需要填参数列表
{
local var=value #局部变量
return 1 #return只能返回整数,不能返回其他类型,返回值作为退出状态来使用
}
function关键字可以省略,小括号也可以省略,但是两个必须保留一个,不然解析器不知道是一个函数
调用函数的方法,就跟普通命令一样
函数名 arg1 arg2…
函数的执行状态看return语句,如果没有return语句,就以函数的最后一条执行的指令的返回状态作为整个函数的退出状态
函数的传参:也是使用$1 $2…来获取函数内的参数
函数支持递归
示例:遍历目录,包括子目录,如果是文件就输出xxx是文件,如果是目录,就输出xxx是目录
#!/bin/bash
function visit()
{
local dir="$1"
for f in `ls $1`
do
if [ -f "$dir/$f" ]
then
echo "$dir/$f is a file"
elif [ -d "$dir/$f" ]
then
echo "$dir/$f is a dir"
visit "$dir/$f"
else
echo "$dir/$f is not recognized"
fi
done
}
visit .
23、shell脚本调试方法
-n 读一遍脚本中的命令但不执行,用于检查脚本中的语法错误
-v 一边执行脚本,一边将执行过的脚本命令打印到标准错误输出
-x 提供跟踪执行信息,将执行的每一条命令和结果依次打印出来
打开调试的方法
- bash -x 脚本.sh
- 脚本开头 使用 #!/bin/bash -x
- 脚本中显式的使用 set -x 打开 set +x 关闭
24、正则表达式
练习:
1.以S开头结尾的字符串
^S
2.以数字结尾的字符串
[0123456789] 匹配任意数字
[0-9]
\d
$ 匹配字符串结尾
[0-9]$
3.匹配空字符串(没有任何字符)
^$
^表示开头
$表示结尾
4.字符串只包含三个数字
^\d\d\d $
或者
^\d{3}$
{n}花括号括起来一个数字,表示前面的单元重复n次
5.字符串只有三到五个字母
控制最少重复次数和最大重复次数
{m,n} m表示前面单元最小重复次数,n表示前面单元最大重复次数[a-zA-Z] 如果中括号有多个区间,区间之间不能留空格或其他分隔符
^ [a-zA-Z]{3,5}$
6.匹配不是a-z的任意字母
[^a-z] 中括号中第一个字符如果是^ ,表示区间取反
^ [^a-z]$
7.字符串有0到1个数字或者字母或者下划线
{0,1} 表示重复0-1次
? 也可以表示0-1次重复
1?$
或
^\w?$
8.字符串有一个或者多个空白符号(\t\r\n等)
\s 表示空白字符 包括 \t\n\r…
{1,} 表示重复1-n 跟+号一样
^\s+$
或
^\s{1,}$
9.字符串有0个或者若干个任意字符(除了\n)
. 代表任意字符, 除了\n
^.{,}$
花括号中两个参数置空表示重复次数任意 0-n
^.*$
*号代表前面的单元重复0-n次
问号? 表示0-1次
加号+ 表示1-n次
星号 * 表示0-n次
10.匹配0或任意多组ABC,比如ABC,ABCABCABC
使用小括号来将多个单元重新组合成一个单元
^(ABC)*$
11.字符串要么是ABC,要么是123
| 表示选择,选择两边的正则匹配一个
^(ABC|123)$ 小括号也可以将选择范围控制在括号内
12.字符串只有一个点号
做转义 还是使用
^.$
13.匹配十进制3位整数
100 -999
^ [1-9][0-9]{2}$
匹配十进制 0-999的数字
分段
一位数 [0-9]
两位数 10-99 [1-9] [0-9]
三位数 [1-9][0-9]{2}
^([0-9] | [1-9][0-9]{1,2})$
14.匹配0-255的整数
匹配ip
分段
一位数 [0-9]
两位数 [1-9][0-9]
三位数100-199 :1[0-9]{2} 200-249: 2[0-4][0-9] 250-255: 25[0_5]
^([0-9] | [1-9][0-9] | 1[0-9]{2}2[0-4][0-9]25[0_5])$
25、sort命令
命令从标准输入中读取数据然后按照字符串内容进行排序
-f 忽略字符大小写
-n 比较数值大小
-t 指定分隔符,默认是空格或tab
-k 指定分割后进行比较字段
-u 重复的行只显示一次
-r 反向排序
-R 打乱排序
将/etc/passwd 根据用户id来排序:sort -t: -k3 -n < /etc/passwd
26、uniq命令
去除重复的行,前提是重复的行连续
-c 显示每行重复的次数
-d 仅显示重复过的行
-u 仅显示不曾重复的行
27、wc,命令
world count
-l 统计行数
-c 统计字数
-w 统计单词
28、grep命令
-c 只输出匹配行的计数
-i 不区分大小写
-H 文件名显示
-r 递归遍历目录
-n 显示行号
-s 不显示不存在或无匹配文本的错误信息
-v 显示不包含匹配文本的所有行,这个参数经常用于过滤不想显示的行
-E 使用扩展的正则表达式
-p 使用prel的正则表达式
-F 匹配固定的字符串,而非正则表达式
egrep = grep -E
fgrep = grep -F
rgrep = grep -r
grep默认使用的是基础的正则
我们在24学的是prel正则,这个比较常用
29、find命令
30、sed命令
ed 主要是用来将数据进行选取、替换、删除、新増的命令。
语法:
[root@localhost ~] # sed [选项] '[动作]' 文件名
选项:
-n:一般 sed 命令会把所有数据都输出到屏幕上。如果加入此选项,则只会把经过 sed 命令处理的行输出到屏幕上;
-e: 允许对输入数据应用多条 sed 命令编辑;
-f 脚本文件名:从 sed 脚本中读入 sed 操作。和 awk 命令的 -f 选项非常类似;
-r:在 sed 中支持扩展正则表达式;
-i:用 sed 的修改结果直接修改读取数据的文件,而不是由屏幕输出动作;
a \:追加,在当前行后添加一行或多行。当添加多行时,除最后一行外,每行末尾需要用“\”代表数据未完结;
c \:行替换,用c后面的字符串替换原数据行。当替换多行时,除最后一行外,每行末尾需用“\”代表数据未完结;
i \:插入,在当前行前插入一行或多行。当插入多行时,除最后一行外,每行末尾需要用“\”代表数据未完结;
d:删除,删除指定的行;
P:打印,输出指定的行;
s:字符串替换,用一个字符串替换另一个字符串。格式为“行范围s/旧字串/新字串/g”(和Vim中的替换格式类似);
行数据操作
查看subandnum.sh 的第五行数据
[root@node02 shell_study]# sed -n '5p' subandnum.sh
do
-n 和 -p 要一起使用
删除第二行到第四行的数据
[root@node02 shell_study]# sed '2,4d' subandnum.sh
#!/bin/bash
do
sum=$[$sum+$1]
shift
done
echo $sum
可以使用$代表最后一行
追加和插入行数据
[root@node02 shell_study]# sed '$a hello' subandnum.sh
#!/bin/bash
sum=0
while [ -n "$1" ]
do
sum=$[$sum+$1]
shift
done
echo $sum
hello
a表示在指定行后追加数据,i表示在指定行前追加数据
[root@node02 shell_study]# sed '$i world' subandnum.sh
#!/bin/bash
sum=0
while [ -n "$1" ]
do
sum=$[$sum+$1]
shift
done
world
echo $sum
行数据替换
[root@node02 shell_study]# cat subandnum.sh | sed '3c sum=1'
#!/bin/bash
sum=1
while [ -n "$1" ]
do
sum=$[$sum+$1]
shift
done
echo $sum
字符串替换
"c"动作是进行整行替换的,如果仅仅想替换行中的部分数据,就要使用"s"动作了。"s"动作的格式如下:
[root@localhost ~]# sed's/旧字符串/新字符串/g' 文件名
把第三行的0换成2
[root@node02 shell_study]# sed '3s/0/2/g' subandnum.sh
#!/bin/bash
sum=2
while [ -n "$1" ]
do
sum=$[$sum+$1]
shift
done
echo $sum
31、awk
语法格式:
awk [选项] ‘条件{动作} 条件{动作} ... ...’ 文件名
awk内置变量:
grep 更适合单纯的查找或匹配文本
sed 更适合编辑匹配到的文本
awk 更适合格式化文本,对文本进行较复杂格式处理
32、crontab
linux 系统定时器
需求,每天什么时候去做什么事
/etc/crontab
命令格式:
m h dom mon dow user command
m -> miunte 0-59
h -> hour 0-23
dom -> day of month 1-31
mon -> month 1-12
dow -> day of week 0-7 其中0和7都表示星期天
示例:
先写一个脚本(脚本路径:/home/admin/network/shell_study/sh/test.sh),把日期写到当前路径的date.txt文件中
#!/bin/bash
#获取当前路径
curPath=$(cd `dirname $0`;pwd)
date >> "$curPath/date.txt"
然后使用命令:
crontab -e 写我们的定时脚本
*/1 * * * * /home/admin/network/shell_study/sh/test.sh
# 每隔一分钟执行test.sh脚本
最后我们查看sh目录下会多出一个date.txt文件。证明定时任务成功
注意:
当输入crontab-e命令时,出现
错误时,修改默认编译器即可
[root@node02 admin]# vim ~/.bashrc
在打开的文件最后一行加入export EDITOR=/usr/bin/vim
最后是修改的文件生效
[root@node02 admin]# source ~/.bashrc
0-9a-zA-Z_ ↩︎