shell
shell创立的目的就是把一些常规的命令封号,本质是c语言写的,编译成二进制,一些shell脚本存放在/usr/bin目录中,打开可以看到是二进制文件
shell有很多种,有sh,bash,zsh等
我们可以把一些命令写到文件当中,下次我们直接执行这个文件方便很多,这种就是shell脚本
在windows中是*.bat
在linux中一般(约定俗成)是.sh文件结尾
shell脚本规则
第一行(SheBang):#!/bin/bash
第一行使用#! 指定shell执行的解释器,后面再出现#号就都是注释了
#!/bin/bash
#!/bin/sh 软链接也指向了bash
#!/usr/bin/python 使用python解释器,比如yum就是用的这个
#!/usr/bin/env 兼容写法
执行shell脚本的方式
bash命令
查看当前用的什么shell解释器 echo $SHELL
print the history story size : echo $HISTSIZE
print the code history : vim ~/.bash_history cat ~/.zsh_history history
history -c :clear the history
history -r : recover the history
!history_id : rapid exec the history cmd
!! : rapid exec the last cmd , also can use the up and down
ctrl+l : rapid cleart the terminal
echo $? : return the last cmd if exec successful ,if return 0 successful ,esle error
变量
变量命名中间不能有空格,比如name = “test”,中间加了空格会分开了,不认识了
变量命名区分大小写,打印变量可以echo $name,完整写法echo ${name},加上了{}
name="test_name"
echo $name # echo ${name}
变量是会有作用域的,在一个子shell的变量会在其它地方找不到,也会有全局变量,就都可以访问了
可以用pstree查看当前在哪个shell里了
让程序在后台跑
example:
nohup vivado &
会返回任务的PID号,需要结束直接kill PID就行
输出内容会存放在nohup.out文件中
其它
单引号不能识别特殊语法,双引号可以识别特殊语法
比如:
name="test" name1='${name}' # name1="${name}" name2="${name}" # name2="test"
bash执行和source执行区别
如果调用bash/sh解释器执行脚本,就会开启一个子shell,因此不会保留当前的shell变量,可以用pstree进程树查看
如果使用source或者. 在当前shell加载脚本,就会保留变量
`反引号`
`反引号`包含起来的就不是字符串型变量了,他会把反引号中的内容进行执行并返回结果
name=`ll` # name 就会是ll执行的结果
环境变量
特殊参数变量
$0 shell脚本名字
$n n可以是1-9,如$1,$2等,如果大于9就需要加{},如${10},参数空格隔开
$# 脚本执行后面的参数总个数
$* 获取shell传过来的所有参数,不加引号等于$@, 带引号会把所有参数合并成字符串,无法分割
$@ 不加引号等于$*, 带引号会把参数分开
$*和$@区别,在下面这段代码就可以看出来来,如果是$*,就一个参数,是$@就能正常分开循环输出
for var in "$*"
do
echo "$var"
done
特殊状态变量
$? 上个脚本执行的返回值
$$ 获取当前脚本的PID号
$! 获取上一次执行的后台脚本的PID、
$_ 获取上次命令的最后一个参数
linux自带特殊命令
echo
-n 不换行输出
-e 解析字符串特殊符号如\n换行等
\n 换行
\r 回车
\t 制表符
\b 退格
example:
echo -e "test\ndemo"
printf
和c语言printf一样用就行
eval
执行多个命令时使用
# 使用分号;是隔开多个命令
eval ll;cd ~/Desktop
exec
不创建子进程,执行后续命令,执行完毕后自动exit
shell子串
${变量} # 返回变量
${#变量} # 返回变量字符长度
${变量:n} # 返回字符串索引n后面的内容
${变量:n:length} # 返回字符串索引n后面length个字符
${变量#word} # 返回从头开始删除最短匹配word的内容 其中word可以使用通配符*等,下面word同理
${变量##word} # 返回从头开始删除最长匹配word的内容
${变量%word} # 返回从尾开始删除最短匹配word的内容
${变量%%word} # 返回从尾开始删除最长匹配word的内容
${变量/pattern/string} # 用string代替第一个匹配的pattern
${变量//pattern/string} # 用string代替所有匹配的pattern
${变量:-word} # 如果变量为空,返回word
${变量:=word} # 如果变量为空,用word代替变量,也会返回word值
${变量:?word} # 如果变量为空,会把word输出报错stderr,否则输出变量值
${变量:+word} # 如果变量为空,什么都不做,否则word,(没弄错,是输出word)
拆分语法
{1…5} 从1到5
example:
touch demo_{1..5}_test.cpp # 会创建五个文件
示例:批量修改文件名
假设有五个文件 touch demo_{1…5}_test.cpp
需要将他们后面的_test删除
#!/bin/bash
for file_name in `ls demo*.cpp`
do
echo $file_name
mv $file_name `echo ${file_name//_test/}`
done
清空变量
name="test"
unset name
子shell
sh就可以进入子shell,使用ps -ef 就可以查看当前进程树
为什么要用子shell:有一些命令比较耗时或者需要一直执行,比如ping google.com等
那么可以把它放到子shell中运行,提高程序并发执行效率。
$BASH_SUBSHELL
如果是0就是当前shell,如果是1就是子shell
使用()包含起来的命令列表,就会在子shell中运行
example:
(cd ~/Desktop;ll;echo $BASH_SUBSHELL) # 我现在用的zsh,就要把BASH换成ZSH
也可以嵌套子shell,(pwd;(pwd;echo $ZSH_SUBSHELL))
内置命令,外置命令
内置命令:
在系统启动时就加载入内存,常驻内存,比如cd等命令,效率高,但是占资源
外置命令:
系统从硬盘中读取程序文件,再读入内存加载,特点:一定会开启一个子进程执行
通过linux的type命令判断属于哪一种命令
type cd
"cd is a shell builtin"
type ps
ps is /usr/bin/ps
比如使用ps命令,是外置命令,会开启子进程执行。
内置命令不会产生子进程执行。
内置命令和shell是一体的,是shell的一部分,不需要单独读取某个文件,系统启动后就存在内容了
查看linux内置命令:compgen -b
shell 数值计算
注意shell只支持整数运算,不支持浮点数
运算使用(( ))包含起来,也可以使用let,但是(( ))效率更高
num=5
((num=num*5)) # 赋值
echo $num # 打印
a++ # 和c相同,下面同理
a--
++a
--a
示例,shell实现求和
#!/usr/bin/bash
# shell 求和
if ((`echo $#`==0))
then
echo "error , please input val"
exit -1
fi
sum=0
for var in "$@"
do
((sum+=$var))
# echo ${var}
done
echo "sum=${sum}"
exit 0
使用:
./shell_sum.sh 10 11 12 13 -333
输出:sum=-287
expr使用
man expr 或者 expr --help 查看手册
使用length等特殊
expr length test # 4
运算
# expr 逻辑运算
expr 5+3
expr 5 + 3
# expr 5 +3 是错的
# expr 5 * 3 是错的,因为有些符号是特殊字符
expr 5 \* 3 # 使用转义\
# expr 逻辑判断
expr 5 \> 6 # 返回0
expr 8 \> 6 # 返回1
模式匹配
expr 存在两个特殊字符。
: 冒号,计算字符串字符数量
.* 重复多次
expr 123456 ":" ".*"
bc命令
数学运算,支持浮点数
bc命令结合管道符
echo "2.2*4" | bc
计算1+2+3+…+100
echo {1..100} | tr " " "+" | bc
&& ||
A && B 当A成立时,会执行B
A || B 当A不成立时,会执行B
test命令
-e 判断文件/目录是否存在 # test -e main.cpp && echo "file is exist" || touch main.cpp
-f 判断文件是否是普通文件
-d 判断是否为目录
-z 判断字符串是否为空,为空返回真
-n 判断字符串是否为空,为空返回假
-r 判断是否可读
-w 判断是否可写
-x 判断是否有可执行权限
[ ] 中括号条件测试
- test和 [] 作用是一样的
- 注意 [ testcode ] 中间需要有空格,否则报错
- 在条件测试中使用变量,必须添加双引号
file_name="main.cpp"
[ -e "$file_name" ] && echo "$file_name is exist" || touch main.cpp
[[ ]] 双中括号
和 [ ] 没啥区别
函数
函数定义写法:
# 第一种
function 函数名() {
函数体
}
#第二种
#使用function关键词时可以省略()
function 函数名 {
函数体
}
#第三种
函数名() {
函数体
}
- 函数定义一定在使用之前。
- 函数中定义的变量为局部变量
- 函数中也有return,执行到return就直接返回断了
- 可以使用$?获取return的返回值
- return 是结束函数的执行,exit是结束shell的执行
- 函数单独写在一个文件里,使用source读取
- 函数内使用locate定义局部变量