什么是Shell
shell 是命令解释器,用于解释用户对操作系统的操作。
shell有很多
cat /etc/shells
CentOS 7 默认使用的shell是bash
UNIX的哲学:一条命令只做一件事情
为了组合命令和多次执行命令,使用脚本文件来保存需要执行的命令
赋予该文件执行权限(chmod u+rx filename)
Shell脚本
Sha-Bang
命令
“#”号开头的注释
chmod u+x filename 可执行权限
执行命令
bash ./filename.sh 会生成一个子进程(不需要执行权限)
./filename.sh 会生成一个子进程,使用Sha-Bang (需要可执行权限)
source ./filename.sh 在当前进程运行
. filename.sh (点之后会产生一个子进程)
比如一个脚本如下:
#! /bin/bash
cd /tmp
在/home目录下运行
bash ./filename.sh 运行,子进程运行cd,然后子进程结束,此时pwd,还是在 /home 而不是/tmp
source ./filename.sh 或者 . filename.sh,这两个在/tmp,不产生子进程
内建命令和外部命令的区别
内建命令不需要创建子进程,内建命令比如source
内建命令对当前shell生效
管道和重定向
Shell管道是Shell中最值得称赞的功能之一,它以非常简洁的形式实现了管道的进程间通信方式,我个人认为Shell处理文本数据的半壁江山都来自于竖线形式的管道。像其它编程语言,打开管道后还要区分哪个进程写管道、哪个进程读管道,为了安全,每个进程还要关闭不用的读端或写端,总之就是麻烦,而Shell的管道非常简洁,竖线左边的就是写管道的,竖线右边的就是读管道的。
管道和信号一样,也是进程通信的方式之一
匿名管道(管道符)是 Shell 变成经常用到的通信工具
管道符是“|”,将前一个命令执行的结果传递给后面的命令
ps | cat
ps aux | grep 'sshd'
echo 123 | psecho 123 |cat |cmd 可以连续使用
注意一下输出重定向符
一个进程默认会打开标准输入、标准输出、错误输出三个文件描述符
输入重定向符号 “<”
read var < /path/to/a/file
read var1 < a.txt
输出重定向符号 ">" ">>" "2>" "&>"
echo 123 > /path/to/a/file 清空再输入
echo 123 >> /path/to/a/file 追加
2> 命令执行过程中有错误则重定向
&> 命令执行过程中无论正确错误都重定向
输入和输出重定向组合使用
cat > /path/to/a/file.sh << EOF
i am $USER
EOF
运行以上三句话,会生成一个file.sh文件,文件里内容为i am $USER
变量
变量的赋值
变量名的命名规则
字母、数字、下划线
不以数字开头
变量的赋值
为变量赋值的过程中,称为变量替换
变量名=变量值
a=123 (不允许出现空格,shell会认为前面不是变量名而是一个命令,比如reboot =1),会重启
使用let为变量赋值
let a=10+20 (尽量少用,效率很低)
将命令赋值给变量
l=ls (用处不大)
将命令结果赋值给变量,使用$() 或者 ''
letc=$(ls -l /etc)
letc='ls /root'
变量值有空格等特殊字符可以包含在 "" 或 '' 中
srring1='hello bash'
string2="hello I'm name"
变量引用和作用范围
变量的引用
${变量名}称作对变量的引用
echo ${变量名} 查看变量的值
${变量名} 在部分情况下可以省略为 $变量名
变量的作用范围
变量的默认作用范围
当前的shell,父进程的变量对子进程无效,子进程的对父进程也无效。
可以使用source
变量的导出
export ,子进程可以获得父进程的变量
export demo_var1="hello subshell"
变量的删除
unset
unset demo_var1
系统环境变量,预定义变量,位置变量
环境变量:每个Shell打开都可以获得到的变量
set 和 env 命令
env | more 查看当前所有的环境变量
echo ${HOME} 查看单个环境变量
$PATH 当前命令的搜索路径
所以要在$PATH 中新增路径,使用PATH=$PATH:新加路径(只对当前终端生效,对子shell生效)
$PS1 当前提示终端
预定义变量
echo $? $$ $0等
$? 指上一条命令是否正确执行,echo $?,正确执行返回0,错误1
$$ 显示当前进程 PID
$0 显示当前进程名称
位置变量
$1 $2 ... ${10},需要有{}
比如有脚本 test.sh]如下:
#! /bin/bash
pos1=$1
pos2=$2
echo $pos1
echo $pos2
执行的时候,./test.sh -a -l
会传参进去,可以对脚本进行简化
echo $1
echo $2
考虑到echo $2 的时候 $2 有可能是空值。所以写成下面这样
pos1=$1
pos2=${2}_
echo $pos1
echo $pos2
如果没有传参,则$2默认为_,规避读入的值是空值。但是这么写,如果传参进去,后面会多一个_,所以可以改成下面这样:
pos2=${2-_}
常见的环境变量
HOSTNAME=control_node
USER=root
HOME=/root
SHELL=/bin/bash
HISTSIZE=1000
SSH_TTY=/dev/pts/2
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
MAIL=/var/spool/mail/root
PWD=/root
LANG=en_US.UTF-8
常见的特殊变量
$1,$2,...,$N:脚本的位置参数
$0:shell或shell脚本的名称
$*:扩展为位置参数,"$*"会将所有位置参数一次性包围引起来,"$*"等价于"$1_$2_$3..."
$@:扩展为位置参数,"$@"会将每个位置参数单独引起来,"$@"等价于"$1" "$2" "$3"...
$#:位置参数的个数
$$:当前Shell的进程PID,在某些子Shell(如小括号()开启的子Shell)下,会被继承。如果可以,建议使用$BASHPID替代$$
$?:最近一个前台命令的退出状态码
$!:最近一个后台命令的进程PID
$-:当前Shell环境的一些特殊设置,比如是否交互式
$_:最近一个前台命令的最后一个参数(还有其它情况,该变量用的不多,所以不追究了)
需要注意一下的👍
关于$* "$*" $@ "$@"的区别,参见如下shell脚本测试:
#!/bin/bash
echo '$*----------':
for i in $*;do echo "<$i>";done
echo '"$*--------"':
for i in "$*";do echo "<$i>";done
echo '$@----------':
for i in $@;do echo "<$i>";done
echo '"$@--------"':
for i in "$@";do echo "<$i>";done
$ chmod +x position_parameters.sh
$ ./position_parameters.sh a b 'c d'
$*---------:
<a>
<b>
<c>
<d>
"$*--------":
<a b c d>
$@----------:
<a>
<b>
<c>
<d>
"$@--------":
<a>
<b>
<c d>
环境变量配置文件
配置文件
/etc/profile
/etc/profile.d/
~/.bash_profile
~/.bashrc
/etc/bashrc
所以经常在 /etc/profile 中新增 export PATH=$PATH:/new/path
su - 切换用户会加载4个文件
su 切换用户只会加载~/.bashrc、/etc/bashrc
数组
定义数组
IPTS=(10.0.0.1 10.0.0.2 10.0.0.3)
显示数组的所有元素
echo ${IPTS[@]}
显示数组元素个数
echo ${#IPTS[@]}
显示数组的第一个元素
echo ${IPTS[0]}
转义和引用
特殊字符:一个字符不仅有字面意义,还有元意(meta-meaning)
# 注释
; 分号
\ 转义符号
" 和 ' 引号
单个字符的转义
\n \r \t 单个字母的转义
\$ \" \\ 单个非字母的转义
引用
"" 双引号,如果里面有变量,会进行解释
'' 单引号不会进行解释
` 反引号
运算符
赋值运算符
= 赋值运算符,用于算数赋值和字符串赋值
使用 unset 取消为变量的赋值
= 除了作为赋值运算符还可以作为测试操作符
算数运算符
基本运算符
+ - * / ** %
使用expr进行计算
expr 4 + 5 (要有空格,只能支持整数)
num1=`expr 4 + 5`
数字常量的使用方法,如果不用特殊方法,a=4+5,其实把 “4+5”字符串赋值给a
let “变量名=变量值”
变量值使用0开头为八进制,0x开头为十六进制
双圆括号是let命令的简化
((a=4+5))
((a++))
echo $((10+20))
特殊符号大全
引号
' 完全引用
" 不完全引用
` 执行命令
括号
() (()) $() 圆括号
单独使用圆括号会产生一个子shell(xyz=123)
数组初始化 IPS=(ip1 ip2 ip3)
[] [[]] 方括号
单独使用方括号是测试(test)或数组元素功能
两个方括号表示测试表达式
<> 尖括号 重定向符号
{} 花括号
输出范围 echo{0..9},会输出0-9所有数字
文件复制 cp -v /etc/passwd /etc/passwd.bak
等同于 cp -v /etc/passwd{,.bak}
运算和逻辑符号
+-*/% 算数运算符
><= 比较运算符
&& || !逻辑运算符
(( 5 > 4 && 6> 5)),然后通过 echo $? 判断
转义符号
\
其他符号
# 注释符
; 命令分隔符
case 语句的分隔符要转义 ;;
:空指令
. 和source命令相同
~ 家目录
, 分隔目录
* 通配符
? 条件测试或通配符
$ 取值符号
| 管道符
& 后台运行
空格
测试和判断
退出程序命令
exit 判断上一条命令是否正常,0或者非0
exit 1或者0 返回10 给shell ,返回值非0位不正常退出
$? 判断当前shell前一个进程是否正常退出
测试命令test
test命令用于检查文件或者比较值
test可以做以下测试
文件测试
整数比较测试
字符串测试
test测试语句可以简化为[]符号
test -f /etc/passwd2 判断文件是否存在并且是个普通文件,-e是文件或者目录,-d目录
[ -d /etc/ ]
[]符号还有扩展写法 [[]] 支持 && || < >
if
if-then 语句的基本用法
if [ 测试条件成立 ] 或 命令返回值是否为0
then 执行相应命令
fi 结束
if-then-else 语句可以在条件不成立时也运行相应的命令
if [ 测试条件成立 ]
then 执行相应命令
else 测试条件不成立,执行相应命令
fi 结束
if [ 测试条件成立 ]
then 执行相应命令
elif [ 测试条件成立 ]
then 执行相应命令
else 测试条件不成立,执行相应命令
fi 结束
嵌套 if 的使用
if 条件测试中可以再嵌套 if 条件测试
if [ 测试条件成立 ]
then 执行相应命令
if [测试条件成立]
then 执行相应命令
fi
fi
case
case 语句和 select 语句可以构成分支
case "$变量" in
"情况1" )
命令...;;;
"情况2" )
命令...;;;
* )
命令...;;;
esac
for
for 循环的语法
for 参数 in 列表
do 执行的命令
done 封闭一个循环
使用反引号或 $() 方式执行命令,命令的结果当做列表进行处理
列表中包含多个变量,变量用空格分隔
for i in {0..9}
for filename in 'ls *.mp3'
对文本处理,要使用文本查看命令取出文本内容
默认逐行处理,如果文本出现空格会当做多行处理
C 语言风格的 for 命令
for((变量的初始化;循环判断条件;变量变化))
do
命令
done
while
while test测试是否成立
do
命令
done
until 循环 与while 循环相反,循环测试为假时,执行循环,为真实循环停止
break continue
break 退出
continue 结束本轮循环
使用循环处理位置参数
命令行参数可以使用 $1 $2 ... ${10}..$n 进行读取
$0 代表脚本名称
$* 和 $@ 代表所有位置参数
$# 代表位置参数的数量
有脚本test.sh如下:
#! /bin/bash
# help display help help
for pos in $*
do
if [ "$pos" = "help"]; then
echo $pos $pos
fi
done
运行语句:
bash test.sh a b c help
while 脚本如下:
#! /bin/bash
while [ $# -ge 1]
do
if [ "$1" = "help" ]; then
echo $1 $1
fi
shift
done
shift能够参数左移
运行语句:
bash test.sh a b c help
自定义函数
函数用于“包含”重复使用的命令集合
自定义函数
function fname(){
命令
}
函数的执行
fname
函数作用范围的变量
local 变量名
函数的参数
$1 $2...$n
checkpid() {
local i
for i in $* ; do
[ -d "/proc/$i" ] && return 0
done
return 1
}
执行时
checkpid 1 或者 checkpid 1 2
echo $?
系统脚本
系统自建了函数库,可以在脚本中引用
/etc/init.d/functions
自建函数库
使用 source 函数脚本文件“导入”函数
source /etc/init.d/functions
echo_success
脚本优先级任务
可以使用 nice 和 renice 调整脚本优先级
避免出现“不可控的”死循环
死循环导致cpu占用过高
死循环导致死机
ulimit -a 可以查看当前终端的使用限制,root用户的话有些限制不会生效
max user processes 用户的最大进程数
任务计划
at 18:31
at > echo hello > /tmp/test.txt
at>
然后ctrl +d 提交
cron
配置方式
crontab -e
查看现有的计划任务
crontab -l
配置格式
分钟,小时,日期,月份,星期,执行的命令
注意命令的路径问题
日志(可以查看计划任务有没有被执行)
/var/log/cron
每个用户都有自己的计划任务目录
/var/spool/cron/用户
任务计划加锁
如果计算机不能按照预期时间运行
anacontab 延时计划任务
flock 锁文件
正则表达式和文本搜索
元字符
. 匹配除换行符外的任意单个字符
* 匹配任意一个跟在它前面的字符
[] 匹配方括号中的字符类中的任意一个
^ 匹配开头
$ 匹配结尾
\ 转义后面的特殊字符
扩展元字符
+ 匹配前面的正则表达式至少出现一次
? 匹配前面的正则表达式出现零次或一次
| 匹配它前面或后面的正则表达式
grep 搜索
grep test /root/test.txt
cd /etc
find passwd
-regex 区分大小写
-iregex 不区分大小写
find /etc -name pass*
find /etc -regex .*wd
类型匹配
find /etc -type f -regex .*wd
时间匹配,最基础的时间戳包括:-atime/-mtime/-ctime
find /etc/ -atime 8 -regex .*wd
删除.txt文件
find * txt -exec rm -v {} \;
sed awk xargs
sed 的基本工作方式
-将文件以行为单位读取到内存(模式空间)
-使用sed的每个脚本对该行进行操作
-处理完成后输出该行
sed 一般用于对文本内容做替换
sed '/user1/s/user1/u1' /etc/passwd
sed 's/old/new/' filename
sed -e 's/old/new/' -e 's/old/new/' filename
sed -i 's/old/new/' -i 's/old/new/' filename
sed -r 's/(a*b)/\1 \1' filename
head -5 /etc/passwd | sed 's/...//'
head -5 /etc/passwd | sed 's/s*sbin //'
sed指令扩展
s/old/new标志位
数字.第几次出现才替换
g,每次出现都进行替换
p,打印模式空间的内容
sed -n 阻止默认输出
w file 将模式空间的内容写入到文件
sed 's/root/!!!/2'
sed 's/root/!!!/g'
sed -n 's/root/!!!/w /tmp/a.txt'
sed '1,3s/root/!!!/2' 第一行和第三行进行替换
sed '1,$s/root/!!!/2' 第一行到最后一行进行替换
寻址可以匹配多条命令
/regular/{ 's/old/new/' ; 's/old/new/'}
可以将选项保存为文件,使用-f加载脚本文件
sed -f sedscript filename
awk 基本用法
awk 一般用于对文本内容进行统计,按需要的格式进行输出
cut 命令:cut -d:-f 1/etc/passwd
awk 命令:awk -F: '/wd$/{print$1}' /etc/passwd
awk '/^menu/{print $0}' /boot/grub2/grub.cfg
awk -F "'" '/^menu/{print $2}' /boot/grub2/grub.cfg
awk -F "'" '/^menu/{print x++, $2}' /boot/grub2/grub.cfg
awk 表达式
系统变量
FS和OFS字段分隔符,OFS表示输出的字段分隔符
RS记录分隔符
NR FNR行数
NF字段数量,最后一个字段内容可以用$NF取出
head -5 /etc/passwd | awk 'BEGIN{FS=":"}{print $1}'
head -5 /etc/passwd | awk 'BEGIN{FS=":";OFS="---"}{print $1,$2}'
head -5 /etc/passwd | awk '{print NR,$0}'
awk '{print FNR,$0}' /etc/passwd /etc/passwd
head -5 /etc/passwd | awk 'BEGIN{FS=":"}{print $NF}'
awk '{if($2>80){print $1 ; print $2}}' kpi.txt
head -1 kpi.txt | awk '{for(c=2;c<=NF;c++) print c}'
head -1 kpi.txt | awk '{for(c=2;c<=NF;c++) print $c}'
xargs是将标准输入转为命令行参数
echo "a b c" | xargs mkdir
大多数情况下xargs命令都是跟着管道一起使用的
,但是,也可以单独使用,在输入xargs按下回车键,
命令行就会等待用户输入,作为标准输入,你可以输入
任意内容然后按下ctrl + d 表示输入结束,这时echo命令就会把前面的结果打印出来
-d指定分隔符
echo -e "a\tb\tc" | xargs -d "\t" echo -n
网友评论