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

常用判断条件

  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


  1. 按照文件类型进行判断
符号 意义
-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

  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中的break1. 最后的“*)”表示默认模式,相当于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语法和其他编程语言大相径庭,尤其是在函数这一块。学这门语言还是要耐心。

posted @   jackie_le  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示