背景
本文用于收集Linux常用命令(基于Centos7),是一个持续更新的博客,建议收藏,编写shell时遇到问题可以随时查阅。
1.Shell类型
shell是用C语言编写的程序,作为命令解释器连接着用户和操作系统内核。常见的shell有sh(Bourne Shell), csh(C-Shelll), ksh(Korn Shell), bash(Bourne Again Shell), 推荐使用bash(易用和免费).
通过cat /etc/shells可以查看系统支持的shell类型:
可在shell脚本顶部指定使用的shell类型,如下表示使用bash类型:
#!/bin/bash
#获取当前目录
current_dir=$(cd `dirname $0`;pwd)
2.shell变量类型
shell中常用的变量类型有字符串、数值、数组类型; shell中没有布尔类型, 零值表示 false,非零值表示 true。
2.1 数值类型
数值包括浮点和整数类型,shell不支持浮点数计算,需要借助三方库bc进行( yum -y install bc )。
[1] 运算
在shell中所有的变量类型默认是字符串类型,因此不能直接计算, 可通过expr表达式进行:
#expr形式,`expr $A + &B`
echo `expr $a + $b`
#说明:+-\* / % (*表示乘号,但是作为通配符,需要使用\转义);运算符左右需要有空格。
$变量或者${变量}用于提取变量的值,但是不能执行表达式,反引号``用于执行表达式并获取结果:
[root@localhost ~]# echo The date is `date`
The date is 2025年 05月 28日 星期三 23:10:43 CST
[root@localhost ~]# echo ${date}
#返回空,将date当做变量来提取值
[2] 比较
# ==比较数字是否相等,!= 表示不相等;
a=0
b=1
if [ $a == $b ] ; then echo "yes" ;else echo "no" ;fi;
if [ $a != $b ] ; then echo "yes" ;else echo "no" ;fi;
在[]中< > >= <=等需要使用转义,而[[]]中不需要转义
[ $a < $b ] 等价于 [[ $a < $b ]]
注意:
(1)[] 和运算符左右两侧必须有空格
(2)可以在一行中写多条shell命令,使用分号分隔
(3)使用#进行单号注释
# 使用关系运算符:
#-eq 相等, -ne不等; -gt大于, -lt小于; -ge大于等于, -le小于等于
if [ $A -eq $B]
then
echo "相等"
else
echo "不相等"
fi
注意:关系运算符的应用范围只支持数值类型,不支持字符串。
在[]或[[]]中可以使用单等号和双等号进行字符串和整型是否相等的判断,但是建议使用-eq等关系运算符。
if [ $a = $b ] ; then echo "yes" ;else echo "no" ;fi;
if [[ $a = $b ]] ; then echo "yes" ;else echo "no" ;fi;
2.2 字符串类型
可以使用单引号和双引号、不用引号表示;单引号不会替换内容,全样输出,双引号会进行变量替换。
shell中没有空类型,未定义的变量默认表示长度为0的字符串,即空字符串。
可以使用 ==
和 !=
来表示是否相等;
[ -z $a ] a的长度为0时返回true; [ -n $a ] a的长度不为0时返回true
[ $a ] 不为空时返回true;
echo ${#a}
用于计算字符串长度
2.3 数组类型
shell中仅支持一维数组,下标从0开始,使用小括号进行包裹,并用空格对元素进行分隔。
# 数组定义
test_arr=(aa 123 bb "")
# 使用下标从数组中取值
echo ${test_arr[0]}
# 获取数组长度(*或者@表示全部元素,#表示取长度)
${#test_arr[*]} 或者${#test_arr[@]}
# 取数组中全部元素(删除#即可)
${#test_arr[*]} 或者${#test_arr[@]}
数组的遍历方式:
test_arr=(a b c 1 2 3 "")
for i in ${test_arr[*]}; do echo $i;done
3.test 表达式
test表达式等价于[], 与[[]]在写法上略有不同;都能实现对数值、字符串类型进行判断,以及对文件的非空、大小、属性等进行判断。
使用逻辑运算符时略有不同:
# []中只能使用 -a和-o和!
# [[]] 中只能使用 && || 和!
[ expression1 -a expression2 ] 等价于 [ expression1 && expression2 ]
[ expression1 -o expression2 ] 等价于 [ expression1 || expression2 ]
[ !expression2 ] 等价于 [[ !expression1 ]]
文件类型:
-e 是否存在;
-f 普通类型(存在且); -d目录(存在且);
-b块设备(存在且); -c字符设备(存在且);
-S套接字(存在且); -L链接(存在且)
文件属性:
-r 可读(存在且);-w可写(存在且);-x可执行(存在且)
案例:
#[]方式
if [ ! -e test1.txt ]; then echo "yes" ;else echo "no"; fi
if [ ! -e test1.txt -a -e test2.txt ]; then echo "yes"; else echo "no"; fi
#[[]]方式
if [[ (-e test1.txt && -e test2.txt) ]]; then echo "yes" ;else echo "no"; fi
if [[ !(-e test1.txt && -e test2.txt) ]]; then echo "yes" ;else echo "no"; fi
if [[ ! (-e test1.txt && -e test2.txt) ]]; then echo "yes" ;else echo "no"; fi
#在[[]]中可通过()改变表达式的优先级
#注意! && || 与表达式之间必须有空格
4.函数定义和使用
4.1 函数定义和传参
函数的定义要优先于函数的调用,函数定义有如下两种方式:
#方式1
function 函数名(){
#函数体
}
#方式2
函数名(){
#函数体
}
函数内部可以通过$符号进行变量的获取,有以下类型:
$$ 获取当前进程号;
$0 获取当前所在文件名;
$1-n 按顺序获取参数(注意索引从1开始);
$# 获取参数长度;
∗ 和 * 和 ∗和@ 获取全部参数
函数调用时直接使用函数名即可,不需要添加括号,传参以逗号进行分隔。
function add() {
echo "dollar0 is $0"
echo "dollar1 is $1"
echo "dollar2 is $2"
echo "dollar# is $#"
echo "dollar* is $*"
echo "dollar@ is $@"
echo "dollar-dollar is $$"
}
add a b
执行结果如下:
4.2 函数的返回值
当函数没有通过return语句显示返回时,使用最后一条语句的执行结果返回:执行成功返回0,否则返回1。
也可以在函数中通过return语句显示返回结果,但是只能返回[0-255]之间的整数(否则按256取模),在调用函数结束后使用$?获取执行结果。
function add() {
return 100
}
add
echo "result is $?"
还可通过标准输出流返回结果,函数内部通过echo将要返回的结果输出到标准输出流,在调用函数的地方使用反引号或者$()进行获取,如下所示:
#!/bin/bash
function sum_int(){
local result=0
for n in $@
do
((result+=n))
done
echo $result
return 0
}
echo $(sum_int 1 2 3 4 5)
4.3 使用其他脚本中定义的函数
将文件定义为sh或者其他文件类型(如.conf),使用 . 路径
或者使用source 文件名
的形式引入。
#test.conf文件
function add() {
return 0
}
##test.sh文件
. ./test.conf #或者 source test.conf
add #调用引入的函数
5.全局变量和局部变量和环境变量
5.1 变量定义与提取
# 定义
a=1
b="abc"
# 提取
echo $a
echo ${a}
注意:等号左右不能有空格。
5.2 shell脚本内的全局变量与局部变量
shell脚本中默认为全局变量,如:
aa='123'
echo "aa is $aa"
function test(){
echo "[function]aa is $aa"
bb='234'
}
test
echo "bb is $bb"
得到结果如下:
[root@localhost sy]# sh test_global.sh
aa is 123
[function]aa is 123
bb is 234
无论是在全局范围定义的变量a还是在方法内部定义的变量b,在当前整个shell脚本中有效。
一般而言,为了防止变量污染,会将方法内的变量通过local关键字定义为局部变量,
使得定义的变量仅在函数块内部有效。案例如下:
function test(){
aa='123'
local bb='234'
}
test
echo "aa is $aa"
echo "bb is $bb"
结果如下:
[root@localhost sy]# sh test_local.sh
aa is 123
bb is
其中a为全局变量,b为方法内部的局部变量,此时在方法外的不可见,$b获取的结果为空。
5.3 环境变量
介绍环境变量之前,有必要简要介绍一下登录方式以及环境变量配置文件。
环境变量配置文件:
centos中环境变量的配置文件有两类:全局配置文件与用户配置文件。
全局配置文件对应: /etc/profile
用户配置文件按优先级:~/.bash_profile
> ~/.bash_login
> ~/.profile
. 按顺序查找并执行,且仅执行第一个。
登录方式:
通过用户名新建一个shell远程连接,连接时,会按照先后顺序依次执行加载全局配置文件和用户配置文件;
当通过切换shell或者使用su切换用户时,不会加载配置文件。
当通过 su - 命令模拟登录来切换用户时,会按照先后顺序依次执行加载全局配置文件和用户配置文件。
5.3.1 所有shell有效
一个shell窗口修改/etc/profile
文件保存后(如添加一个变量a=1
),执行source /etc/profile
在当前shell生效。
新开shell窗口时,由于会自动执行source /etc/profile
,从而可以读取变量a的值为1;
对于已经打开的shell窗口,需要再次执行source /etc/profile
.
上述配置不区分用户,如果需要对指定用户生效,需要修改用户配置文件,如~/.bash_profile
5.3.2 export关键字
默认情况下,环境变量(无论是全局还是用户)仅对主进程有效,可通过export进行暴露,对全部子shell和子进程有效,案例如下所示:
[step1] 在/etc/profile
中添加如下两行变量定义:
export NAME1=sys1
NAME2=sys2
[step2] 执行source /etc/profile
使得改动生效:
source /etc/profile
[step3] 主进程查看变量:
[root@localhost sy]# echo $NAME1
sys1
[root@localhost sy]# echo $NAME2
sys2
[step4] 切换用户并查看变量:
[root@localhost sy]# su centuser
[centuser@localhost sy]$ echo $NAME1
sys1
[centuser@localhost sy]$ echo $NAME2
[centuser@localhost sy]$
此时,NAME2不可见,而通过export暴露的NAME1可见。
注意:不仅仅是切换用户,切换shell或者创建的子进程机制相同。
除了环境变量,其他变量也是如此:父进程定义的变量默认对子进程不可见,可通过export进行暴露:
[centuser@localhost sy]$ test_a='aa'
[centuser@localhost sy]$ export test_b='bb'
[centuser@localhost sy]$ sh
sh-4.2$ echo $test_a
sh-4.2$ echo $test_b
bb
6.if结构和case结构
6.1 if结构
#1.if
if 表达式; then
#逻辑
fi
#2.if-else
if 表达式; then
#逻辑1
else
#逻辑2
fi
#3.if-elif-fi
if 表达式1; then
#逻辑1
elif 表达式2; then
#逻辑2
else
#逻辑3
fi
6.2 case结构
case VAR in
var1)
command1
;;
var2)
command2
;;
var3)
command3
;;
*)
command-default
;;
esac
注意:shell中的case不存在穿透问题
案例介绍:
case "$1" in
"restart")
echo "restart success"
;;
"stop")
echo "stop success"
;;
"start")
echo "start success"
;;
*)
echo "parameter error!! ";
echo "three parameters are valid----restart, start, stop";;
esac
7.for和while循环结构
7.1 for循环结构
for 变量名 in 取值列表
do
命令
done
#{1..5}为取值范围1到5
for i in {1..5};do echo $i;done
for((i=1;i<=5;i++));do echo $i;done
#遍历数组:
test_arr=(1 2 3 4 5)
for i in ${test_arr[*]}; do echo $i;done
7.2 while循环结构
#当条件测试成立(条件测试为真),执行循环
while 条件测试
do
循环体
done
i=1
while [ $i -le 10 ]
do
echo $i
let i++
done
8.文件操作
8.1 创建文件和文件夹
#创建文件
touch 文件名
> touch a.txt
# 创建文件夹: 如果文件夹已存在或者某一路径不存在,报错
mkdir 文件夹
>mkdir dira
>mkdir /home/test/dira
#递归创建文件夹,如果文件夹或者某一路径不存在创建,存在则不创建(不报错)
mkdir -p 文件夹
>mkdir -p /home/test/dira
8.2 编辑、复制、移动、删除
8.2.1 文件编辑与查询
可以通过vi与vim对文件进行编辑,推荐vim.
#显示行号
:set nu
#不保存修改退出
:q!
#保存并退出
wq!
#根据关键词从底部开始向上搜索,按N或者shift+N进行定位
? 关键词
#i或者insert键可以将非编辑状态切换至编辑状态(插入和替换)
#非编辑状态下:"dd"删除一行;"d+数字N+回车"删除N行
8.2.2 复制
cp命令的参数:
-r 表示递归操作, 适用于进行目录复制;
-p 保留元属性,如 修改时间、访问时间和权限模式;
#1.复制文件
cp source_dir dest_dir
#2.复制目录
cp -r source_dir dest_Dir
#3.复制多个文件或文件夹
cp -r source_dir/* dest_Dir
8.2.3 移动
# 修改名称
mv 源文件 目标文件
>mv a.txt b.txt
# 移动路径,不替换名称
mv 源文件 路径/
>mv a.txt /home/test/
# 移动路径&&替换名称
mv 源文件 路径/目标文件
>move a.txt /home/test/b.txt
8.2.4 删除
# 删除文件或文件夹
rm 文件
>rm a.txt
# 删除文件夹(递归删除文件夹文件)
rm -r 文件夹
>rm -r /home/test
# 强制删除文件或文件夹(不会交互提示-是否删除)
rm -rf 文件/文件夹
>rm -rf /home/test
8.3 压缩与解压缩
zip类型的压缩和解压缩:
#压缩文件
[root@localhost test]# zip test.zip test.txt
adding: test.txt (stored 0%)
#递归压缩文件夹
[root@localhost home]# zip -r test.zip /home/test/
adding: home/test/ (stored 0%)
adding: home/test/test.txt (stored 0%)
adding: home/test/test.zip (stored 0%)
adding: home/test/test1.txt (stored 0%)
adding: home/test/test2.txt (stored 0%)
adding: home/test/test3.txt (stored 0%)
#zip压缩时会携带路径,因此可通过相对路径进行压缩。
#比如:对路径下所有文件进行打包(解压时-不会携带路径)
[root@localhost test]# zip -r test.zip ./
updating: test.txt (stored 0%)
adding: test1.txt (stored 0%)
adding: test2.txt (stored 0%)
adding: test3.txt (stored 0%)
#解压当前路径下的zip包到当前文件夹
unzip test.zip
#解压其他路径的zip包到当前文件夹
unzip /home/test.zip
tar类型的压缩和解压缩:
## =================使用 -zcvf 压缩=================
# 携带路径压缩
[root@localhost test]# tar -zcvf test.tar.gz /home/test
tar: Removing leading `/' from member names
/home/test/
/home/test/test.txt
/home/test/test1.txt
/home/test/test2.txt
/home/test/test3.txt
tar: /home/test: file changed as we read it
# 不携带路径压缩,如对当前路径进行打包
[root@localhost test]# tar -zcvf test.tar.gz ./
./
./test.txt
./test1.txt
./test2.txt
./test3.txt
tar: .: file changed as we read it
## =================使用 -zxvf 解压缩=================
# 解压缩当前路径下的test.tar.gz包到当前路径
tar -zxvf test.tar.gz
# 解压缩home/sheng路径下的test.tar.gz包到当前路径
tar -zxvf /home/sheng/test.tar.gz
9.属组和权限操作
通过ls或者ll可以查看文件的属组和权限信息,如下所示:
[root@localhost test]# ll -t
total 4
-rwxrw-r--. 1 root root 4 May 29 11:10 test.txt
此时,test.txt的属组为root:root, 文件权限为rwxrw-r–.
其中:rwxrw-r–按照三个一组按顺序分别表示当前用户(rwx),群组内用户(rw-),其他用户(r–); 此时当前用户可读可写可执行,群组内用户可读可写不可执行,其他用户可读不可写不可执行。
rwx也可以用位进行表示,r表示4,w表示2,x表示1;则rwx=7,rw-=6,r-x=5,r–=4.
对于目录而言,权限与文件略有不同:
目录如果没有执行权限,则无法cd切换到该目录,且该目录以及递归子目录与文件都没有任何权限(通过ls+全路径查询也不行); 如果没有可写权限,则无法在目录下创建/删除文件或者文件夹;如果没有可读权限,无法获取目录下文件或文件夹的信息,但是不影响执行与修改。
另外,可通过修改umask修改系统的默认权限,当创建文件时,系统将使用修改后的默认权限。
[root@localhost test]# umask
0022
此时,umask权限掩码为022,则默认创建的文件夹的权限为755( 777-022=755),即rwxr-xr-x;
文件由于默认不具备执行权限,扣除执行权限,为rw-r–r–
修改umask掩码为020后创建文件,查看文件的默认权限:
[root@localhost test]# umask 020
[root@localhost test]# umask
0020
[root@localhost test]# touch a.txt
[root@localhost test]# mkdir dirb
[root@localhost test]# ll
total 0
-rw-r--rw-. 1 root root 0 May 29 14:54 a.txt
drwxr-xrwx. 2 root root 6 May 29 14:54 dirb
此时,文件的默认权限为(777-020=757), 即目录默认权限为rwxr-xrwx,文件默认权限为rw-r–rw-
9.1 修改属组
#修改文件属组"chown 用户:群组 文件名"
chown test:test a.txt
# 递归修改文件夹内所有文件的属组
chown -R test:test /home/test
9.2 修改权限
可对文件进行读、写、执行权限的添加和删除;
# 对test.sh文件(所有用户)添加可执行权限
chmod +x test.sh
# 仅对当前用户添加可执行权限(u表示所属用户,g表示群组,o表示其他用户)
chmod u+x test.sh
#还可通过数字进行权限修改
chmod 777 test.sh
#-R 对文件夹递归修改权限
chmod -R 777 test.sh
10.kill
kill指令用法如下:
kill [signal] PID
#pid是进程的ID
#signal一般有TERM(默认), KILL(-9), INT(-2), STOP(-17), CONT(-18)
#常用的方式有
kill pid #向进程pid发送TERM信号,请求进程正常退出,进程可以在完成清理工作后退出;
kill -9 pid #向进程pid发送KILL信号,强制进程退出,可能导致数据丢失和文件损失问题
11.常用工具函数
11.1 properties文件读取
version.properties文件如下:
aiws.versionCode=1.0
对应提取方法如下:
propvalue()
{
grep ${1} $project_dir/version.properties | cut -d'=' -f2 | sed 's/\r//'
return $?
}
version_code=$(propvalue aiws.versionCode)
12.常见指令
其他常见执行sleep、awk、sed、expr、exec、dirname…等的用法将陆续更新。