shell编程2
shell编程入坑2
shell内置命令与外置命令
内置命令:在系统启动时加载到内存中,常驻内存,执行效率更高,但是占用资源。
内置命令相当于是shell的一部分,不需要单独去读取某个文件,系统启动后,就执行在内存中了。
- 使用
type 命令
可以查看一个命令是否是内置还是外置命令:
┌──(kali㉿kali)-[~/桌面]
└─$ type cd
cd 是 shell 内建
- 使用
compgen -b
可以查看linux所有内置的命令。
外置命令:系统需要从硬盘中读取程序文件,再读入内存加载。
外置命令是存在于shell之外的程序。外部命令程序通常位于
/bin
,/usr/bin
,/sbin
或/usr/sbin
中。
使用which可以查看命令的位置
┌──(kali㉿kali)-[~/桌面]
└─$ which ps
/usr/bin/ps
┌──(kali㉿kali)-[~/桌面]
└─$ type ps
ps 是 /usr/bin/ps # 说明ps是外置命令
当外部命令执行时,父进程会衍生一个子进程,由该子进程执行该外部命令。比如下面执行ps -f --forest
,可以看出ps -f --forest
执行时PID为107548,而其PPID为15837,也正是zsh shell的PID,说明这一过程中,shell衍生了子进程来执行ps -f --forest
。
UID PID PPID C STIME TTY TIME CMD
kali 15837 15831 0 19:00 pts/0 00:00:00 /usr/bin/zsh
kali 107548 15837 0 22:54 pts/0 00:00:00 \_ ps -f --forest
bash部分基础命令
- 内置
echo
作用:向窗口输出命令,结尾默认输出换行
两个重要参数
参数 | 意义 |
---|---|
-e | 输出时解析字符串中的特殊符号 |
-n | 不换行输出 |
┌──(root㉿kali)-[/home/kali/桌面]
└─# echo -n "123456"
123456
┌──(root㉿kali)-[/home/kali/桌面]
└─# echo -n "123456";echo -n "123456"
123456123456
┌──(root㉿kali)-[/home/kali/桌面]
└─# echo "123\n456"
123\n456
┌──(root㉿kali)-[/home/kali/桌面]
└─# echo -e "123\n456"
123
456
printf
作用:和echo类似,也是向窗口输出指定文本,具体语法与C语言中的printf输出函数类似。
用法:
└─# printf
printf:用法:printf [-v var] 格式 [参数]
└─# name="j";age=31;printf "my name is %s;\n my age is %d" $name $age
my name is j;
my age is 31
eval
作用:可以执行多个命令
└─# eval ls;pwd
1 参数传递.sh haha hahar hello.txt test.sh tmux.txt
/home/kali/桌面
其实发现,也可以不加eval直接运行命令。
exec
作用:不创建子进程,执行后续命令,且执行完毕后,自动exit
┌─(root㉿kali)-[/home/kali/桌面]
└─# useradd jackie
┌──(root㉿kali)-[/home/kali/桌面]
└─# su jackie # 进入其他用户的进程
$ exec date
2022年 08月 31日 星期三 18:30:36 CST
# 使用exec运行date命令后,可以发现退出了当前用户的shell进程
┌──(root㉿kali)-[/home/kali/桌面]
└─#
read
作用:一个一个词组地接收输入的参数,每个词组需要使用空格进行分隔;如果输入的词组个数大于需要的参数个数,则多出的词组将被作为整体为最后一个参数接收。
read.sh
内容如下:
#! /bin/bash
read first second
echo "接受的第一个参数为:$first,第二个参数为:$second"
执行:
┌──(kali㉿kali)-[~/桌面]
└─$ ./read.sh
a b c d
接受的第一个参数为:a,第二个参数为:b c d
- 外置
expr
作用:一般用于整数值计算,但也可用于字符串操作。
使用格式:expr argument operator argument
参数说明:argument:为第一个参数
operator:为操作运算符
argument:为第二个参数
# 普通数值计算,需注意运算符左右要留空格,计算的数值要为整数
expr 10 + 20
30
expr 10 - 20
-10
# *号在shell中有其他特殊的含义,如果要计算乘法,使用\将其转义.
expr 5 \* 2
10
# 用于字符串操作,如计算字符串长度
expr length "abcd"
4
shell子串的一些用法
部分相关特殊用法:
注:x为一个字符串的变量名,下标从0开始。支持通配符。
符号 | 含义 |
---|---|
${x}或$x | 返回变量x的值 |
$ | 返回变量值的长度,即字符长度 |
$ | 返回变量x中从第start个字符开始,到最后的部分。需要注意的是:start的取值在0到${#var}-1之间 |
$ | 返回变量x中从第start个字符开始长度为len的部分 |
$ | 从开头删除最短匹配的子串 |
$ | 从开头删除最长匹配的子串 |
$ | 从结尾删除最短匹配的子串 |
$ | 从结尾删除最长匹配的子串 |
$ | 用string代替第一个匹配的pattern |
$ | 用string代替所有的pattern |
$ | 如果parameter变量值为空,返回word字符串 |
$ | 如果parameter变量值为空,则word替代变量值,且返回其值 |
$ | 如果parameter变量值为空,word当作stderr输出,否则输出变量值。用于设置变量为空导致错误时,返回的错误信息 |
$ | 如果parameter变量为空,什么都不做,否则返回word的值 |
测试文件test内容如下:
#! /bin/bash
name="http://www.baidu.com"
echo -e '${name}:'"\t${name}"
echo -e '${#name}:'"\t${#name}"
echo -e '${name:7}:'"\t${name:7}"
echo -e '${name:11:5}:'"\t${name:11:5}"
string="abcdeabcdeabcdeabcd"
echo -e '${string}'"\t${string}"
echo -e '${string#a*e}'"\t${string#a*e}"
echo -e '${string##a*e}'"\t${string##a*e}"
echo -e '${string%c*d}'"\t${string%c*d}"
echo -e '${string%%c*d}'"\t${string%%c*d}"
echo -e '${string/abcde/ae}'"\t${string/abcde/ae}"
echo -e '${string//abcde/ae}'"\t${string//abcde/ae}"
运行:
./test
${name}: http://www.baidu.com
${#name}: 20
${name:7}: www.baidu.com
${name:11:5}: baidu
${string} abcdeabcdeabcdeabcd
${string#a*e} abcdeabcdeabcd
${string##a*e} abcd
${string%c*d} abcdeabcdeabcdeab
${string%%c*d} ab
${string/abcde/ae} aeabcdeabcdeabcd
${string//abcde/ae} aeaeaeabcd
计算变量长度的各种方式
name="http://www.baidu.com" # 统计变脸name值的长度
# 法一 wc命令
echo "${name}" |wc -L (-L表示统计所有行中的最大长度,也就可以用来统计单个变量的长度)
20
# 法二 利用数值计算expr命令
expr length "${name}"
20
# 法三 awk命令
echo "${name}" |awk '{print length($0)}'
20
# 法四 最快的方法:${#name}
echo "${#name}"
20
数据计算的命令
shell的逻辑运算符与算符运算符和其他语言大致一样,这里就不展开将了,简要列一下shell特有的相关命令和操作符。
操作符或命令 | 含义 |
---|---|
$(()) | 用于整数运算的常用运算符,效率很高,在前面加上$符号可以取出计算结果 |
let | 用于整数运算,类似于(()) |
expr | 可用于整数运算,但还有很多其他的额外功能,上面已经介绍过 |
bc | Linux下的一个计算器程序,适合整数及小数运算,并不是内置的使用前需要安装。 |
$[] | 用于整数运算,类似于$[] |
awk | awk既可以用于整数运算,也可以用于小数运算 |
declare | 定义变量值和属性,-i参数可以用于定义整型变量,做运算 |
$(())与$[]更为常用。
┌──(root㉿kali)-[/home/kali/桌面]
└─# echo $[123+456]
579
┌──(root㉿kali)-[/home/kali/桌面]
└─# echo $((123*456))
56088
条件判断
基本语法
test condition
[ condition ]
(’condition‘是判断条件,前后留有空格。)
使用上面方式进行对式子条件判断后,需要使用echo $?
显示上面判断的结果,如果是0即为真,如果是1即为假(与其他编程语言的true为1,false为0不同)。
┌──(root㉿kali)-[/home/kali/桌面]
└─# name=haha
┌──(root㉿kali)-[/home/kali/桌面]
└─# test $name = haha # 注意符号两边也要留有空格
┌──(root㉿kali)-[/home/kali/桌面]
└─# echo $?
0
┌──(root㉿kali)-[/home/kali/桌面]
└─# [ $name = Haha ]
┌──(root㉿kali)-[/home/kali/桌面]
└─# echo $?
1
常用判断条件
- 用于两个整数之间比较
符号 | 意义 |
---|---|
-eq |
等于(equal) |
-ne |
不等于(not equal) |
-lt |
小于(less than) |
-le |
小于等于(less equal) |
-gt |
大于(greater than) |
-ge |
大于等于(greater equal) |
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# a=20
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ $a -lt 15 ]
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
1
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ $a -ne 21 ]
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
0
2.按照文件权限进行判断
符号 | 意义 |
---|---|
-r |
有读的权限(read) |
-w |
有写的权限(write) |
-x |
有执行的权限(execute) |
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# touch test
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ -r test ]
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
0
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ -w test ]
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
0
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ -x test ]
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
1
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ls
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# touch test
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ -r test ]
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
0
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ -w test ]
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
0
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ -x test ]
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ls -lah
总用量 8.0K
drwxr-xr-x 2 root root 4.0K 9月 7 19:42 .
drwxr-xr-x 3 kali kali 4.0K 9月 7 12:26 ..
-rw-r--r-- 1 root root 0 9月 7 19:42 test
- 按照文件类型进行判断
符号 | 意义 |
---|---|
-e |
文件存在(existence) |
-f |
文件存在并且是一个常规的文件(file) |
-d |
文件存在并且是一个目录(directory) |
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ls
test
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ -e test ] # 是否存在文件test?
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
0
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ -f test ] # test是否存在?是否是一个常规文件?
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
0
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ -d test ] # test是否存在?是否是一个常规目录?
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# echo $?
1
- 多条件判断(通过逻辑运算符:&&和||)
&&表示前一条命令执行成功时,才执行后一条命令,||表示上一条命令执行失败后,才执行下一条命令。
像C语言java语言都有三元运算符? :
如果要在shell编程中实现这种三元运算符的效果,可以通过&&和||两个逻辑运算符实现。
例如:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# a=15
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ $a -lt 20 ] && echo "$a < 20" || echo "$a >= 20"
15 < 20
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# [ abc ] && echo "have something" || echo "nothing"
have something
流程控制
if
基本语法:
(1)单分支
if [条件判断式];then
程序
fi
或者
if [条件判断式]
then
程序
fi
(2) 多分支
if [条件判断]
then
程序
elif [条件判断式]
then
程序
fi
示例:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# cat if.sh
#! /bin/sh
if [ $1 = world ];then
echo "hello,$1!"
fi
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./if.sh world
hello,world!
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# a=21
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# if [ $a -gt 18 ] && [ $a -lt 35 ] ; then echo "you have become an adult";fi
you have become an adult
如果要将逻辑运算符也写入中括号[]中,不能用&&和||,而应分别用-a(and) 和-o(or),如:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# if [ $a -gt 18 -a $a -lt 35 ] ; then echo "you have become an adult";fi
you have become an adult
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# cat grade.sh
#! /bin/sh
if [ $1 -lt 60 ];then
echo "不及格";
elif [ $1 -ge 60 -a $1 -lt 80 ];then
echo "良"
else
echo "优"
fi
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./grade.sh 81
优
case
相比于其他语言的case语法,shell的case用法简直逆天。
基本语法:
case $变量名 in
"值1")
如果变量的值等于值1,执行程序1
;;
"值2")
如果变量的值等于值2,执行程序2
;;
……
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac # 这里和上面if的用法一样,将case反过来写成esac就成了结束词。
示例:
1. case行尾必须为单词”in“,每一个模式匹配必须以右括号”)“结束。
1. 双分号“;;”表示命令序列结束,相当于java中的break。
1. 最后的“*)”表示默认模式,相当于java中的default
如:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# cat grade2.sh
#! /bin/sh
case $1 in
1)
echo "this is one"
;;
2)
echo "this is two"
;;
3)
echo "this is three"
;;
*)
echo "no other choice"
;;
esac
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./grade2.sh 2
this is two
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./grade2.sh 4
no other choice
感觉这个版本的case
语法是最奇怪的。
for
基本语法1:
for ((初始值;循环控制条件;变量变化))
do
程序
done
这种双小括号方式的for循环语法,有些shell不支持,比如bash是支持这种for循环语法的,而dash就不支持。
示例:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# cat sum.sh
#! /bin/bash
for ((i=1; i<=$1; i++))
do
sum=$[ $sum + $i ]
done
echo $sum
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./sum.sh 100
5050
基本语法2:
for 变量 in 值1 值2 值3 ……
do
程序
done
还是推荐用这种语法的for循环
示例:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# cat for2.sh
#! /bin/sh
for i in apple banana grape
do
echo "I love $i"
done
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./for2.sh
I love apple
I love banana
I love grape
在shell中,花括号{}
有特殊的用法,可以用来表示一个序列
如:{1..100}
表示 “1到100的序列:1 2 3 4 5 6……”
┌──(kali㉿kali)-[~/桌面/scripts]
└─$ cat for2.sh
#! /bin/bash
for i in {1..100}
do
sum=$[$sum+$i]
done
echo sum=$sum
┌──(kali㉿kali)-[~/桌面/scripts]
└─$ ./for2.sh 100
sum=5050
学完for循环
以后就可以更好的看清楚之前说过的$*
和$@
的区别
详细可以看我上一篇写的关于shell的文章:https://www.cnblogs.com/jackie-lee/p/16643515.html中的内容。
while
基本语法:
while [条件判断]
do
程序片段
done
示例:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# cat while.sh
#! /bin/bash
i=0
while [ $i -le $1 ]
do
sum=$[ $sum + $i ]
i=$[$i+1]
done
echo $sum
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./while.sh 100
5050
以上循环体中的计算式子写法看上去十分反人类,其实,shell并不是为了能让我们可以方便实现复杂编程而设计的一门高级语言,它本身是为了我们能够通过一些简单的命令来控制linux底层的内核,做一些底层操作,所以,语法相对其他语言更加底层。
现在很多shell支持另外的写法,可以按照下面的方式写,使用let
命令,看上去相对简单点:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# cat while2.sh
#! /bin/bash
i=0
while [ $i -le $1 ]
do
let sum+=i
let i++
done
echo $sum
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./while2.sh 100
5050
当然只是一部分shell支持这个简洁的写法,建议两个都要掌握。
read 命令详解
基本语法:
read (选项) (参数)
1)选项:
-p
:指定读取值时的提示符;
-t
:指定读取值时等待的时间(秒),如果-t
不加表示一直等待
2)参数
变量:指定读取值的变量名
示例:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# cat read.sh
#! /bin/bash
read -t 7 -p "Enter your name in seconds:" name
echo welcome $name
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./read.sh
Enter your name in seconds:jackie
welcome jackie
说明:
read -t 7 -p "Enter your name in seconds:" name
指定时间7秒,7秒后停止read
;
指定了给用户看的提示符;
name是接收读取用户输入值的变量名
函数
系统函数
basename
基本语法:
basename [string/pathname][suffix]
(功能描述:basename
命令会删掉所有的前缀,包括最后一个('/')字符,然后将字符串显示出来。
basename
可以理解为取路径里的文件名称,和python中的os.path.basename(path)
的函数功能一样
选项:
suffix
为后缀,如果suffix
被指定了,basename会将pathname或string中的suffix去掉
示例:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# basename /home/kali/桌面/scripts/read.sh
read.sh
dirname
基本语法:
dirname 文件绝对路径
(功能描述:从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后退回剩下的路径(目录的部分))
dirname
可以理解为取文件路径的绝对路径名称,和python中的os.path.dirname(path)
的函数功能一样
示例:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# dirname /home/kali/桌面/scripts/read.sh
/home/kali/桌面/scripts
在脚本中打印出当前文件名和文件绝对路径:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# cat path.sh
#! /bin/bash
echo script name: $(basename $0 .sh)
echo script path: $(cd $(dirname $0);pwd) # 由于$0的值由脚本调用时采用的路径来决定的,所以可能是相对或绝对路径,需要先借助dirname $0切换到脚本所在目录,然后利用 pwd获得脚本的绝对路径。
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./path.sh
script name: path
script path: /home/kali/桌面/scripts
┌──(root㉿kali)-[/home/kali/桌面]
└─# ./scripts/path.sh
script name: path
script path: /home/kali/桌面
date
显示当前时间
后面加上参数后: date +%s
表示当前的时间戳
示例:
根据当前时间生成日志文件名:
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# cat log.sh
#! /bin/bash
filename="$1"_log_$(date +%s)
echo $filename
┌──(root㉿kali)-[/home/kali/桌面/scripts]
└─# ./log.sh jackie
jackie_log_1662725990
自定义函数
基本语法:
[function] 函数名[()]
{
Action
[return int;]
}
1)必须在调用函数地方之前,先声明函数,shell脚本是逐行运行,不会像其他语言一样先编译
2)函数返回值,可以通过
return
返回,通过$?
获取该值,如果不加return
,将以函数体最后一条命令运行结果作为返回值,return
返回的数值大小在(0-255)3)其中的
function
和小括号()
是可选的,参入的参数值默认为$n
形式。4)调用函数时,和脚本传入参数形式一样:
函数名 参数1 参数2 ……
示例:
计算两个输入参数的和:
┌──(root㉿kali)-[/home/kali/桌面]
└─# cat add.sh
#! /bin/bash
function add(){
sum=$[$1 + $2]
return $sum
}
read -p "请输入第一个整数:" a
read -p "请输入第二个整数:" b
add $a $b
echo "a+b:"$?
调用add.sh脚本:
┌──(root㉿kali)-[/home/kali/桌面]
└─# ./add.sh
请输入第一个整数:100
请输入第二个整数:120
a+b:220
输出正常。
但是如果返回的结果sum
值大于255呢?
┌──(root㉿kali)-[/home/kali/桌面]
└─# ./add.sh
请输入第一个整数:200
请输入第二个整数:100
a+b:44
显然超过了255,返回的值会是溢出255的部分。
如果希望函数能够返回超过255的值呢?可以按照如下操作:
┌──(root㉿kali)-[/home/kali/桌面]
└─# cat add2.sh
#! /bin/bash
function add(){
let sum=$1+$2
echo $sum
}
read -p "请输入第一个整数:" a
read -p "请输入第二个整数:" b
sum=$(add $a $b)
echo "两个整数相加的值为:"$sum
函数体中的最后一个命令通过echo
将要返回的值回显出来,然后函数体外通过$()
调用函数add来接收回显出来的值即可。
┌──(root㉿kali)-[/home/kali/桌面]
└─# ./add2.sh
请输入第一个整数:100
请输入第二个整数:200
两个整数相加的值为:300
不得不说看到这里感觉shell语法和其他编程语言大相径庭,尤其是在函数这一块。学这门语言还是要耐心。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具