shell是什么?
是一个命令解析器(程序),可以解析用户的输入,交给操作系统内核执行,并且把结果反馈给用户,本身也是一个程序我们可以把很多需要执行的命令(操作),以一种语言的方式组织起来,交给shell去运行(解析)
========>shell脚本语言
命令解析器(shell)本质也是一个程序
cat /etc/shells# /etc/shells: valid login shells
/bin/sh
/bin/bash <---------
/bin/rbash
/bin/dash上面都是shell脚本的解释器程序
编译型语言(C/C++)
解释型语言(shell,python,js)
一、写第一个最简单的脚本文件
shell脚本文件以.sh结尾
#!/bin/bash echo "hello shell"
第一行都是固定的: #!/bin/bash
指定由/bin目录下面的哪一个解释器来解析这个脚本文件
脚本中以#开头的行是注释(第一行除外)shell脚本是不需要编译的,可以直接解释运行,但是需要可执行权限
chmod +x 01.sh注意:不要在windows下创建shell脚本文件(01.sh),可能编译不能通过,在虚拟机中使用 vim创建
二、shell中变量的使用
shell变量没有类型的概念,都是字符串
变量也不需要定义,可以直接使用
变量名=值 #=两边不能有空格
name=zhangsan
x=123
x=abc
...如何引用变量:
$变量名 or ${变量名}
shell变量有三种:
1. 自定义变量(自己定义的变量)
如:
name=zhangsan
x=123
x=abc
...shell中可以使用普通的shell命令
d=`date` 反撇号(Esc的下面)
作用:引用反撇号里面那一个“变量/命令/程序”的输出结果(原先输出到标准输出的内容)也可以是程序:xxx=`/mnt/hgfs/文件共享/笔记-代码/1`
shell中的变量都是字符串,可以直接写到一起,表示连接#!/bin/bash x=zhangsan y=lisi z=$x$y echo $z
2. 位置变量
相当于C语言里面的argc和argv
指的是传递给脚本的参数或者传递给函数的参数,参数是按照传递位置排列的
如:
./01sh 123 abc 456
123 abc 456 就是传递给脚本的参数$0 第0个参数,命令行的程序名称(./01sh)
$1,$2,$3....${10} 表示第一个到第10个参数$# 表示命令行参数的个数(不包括脚本的名称)
$@ 表示所有的命令行参数(一个列表),以空格隔开
$@ =======> "$1" "$2" "$3" ....
$* 表示所有的命令行参数(一个数据),以空格隔开
$* =======> "$1 $2 $3 ...."$? 表示前一个命令/函数/程序的退出码(返回值)
3. 环境变量:系统中一些其他程序需要的默认的变量
shell解释器可以引用一些外部的变量(不需要定义,可以直接使用)
如:
HOME : 用户的家目录的路径echo $HOME ===> /home/china
....
需要重点记住的:
LD_LIBRARY_PATH 动态库的搜索路径
PATH 命令或者可执行程序的搜索路径
有没有发现一个问题?
ls也是一个程序,cd也是一个程序,自己编译出来的a.out也是一个程序
为什么我们在运行ls和cd的时候,不需要指定路径,系统就可以找到这个程序并且运行,而我们自己写的a.out就必须指定路径,否则找不到程序shell会自动到指定的路径下面去查找你输入的命令,如果找到了就执行,如果找不到就报错
"路径": PATH
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/arm/5.4.0/usr/bin:/snap/bin:/home/china:
/usr/local/arm/gcc-linaro-6.5.0-2018.12-x86_64_arm-linux-gnueabi/bin/
指定系统要搜索的命令和可执行文件的目录列表
PATH:dir1:dir2:.....我们也可以修改PATH的值,修改环境变量都是使用export
export 环境变量的名称=值
如:
export PATH=$PATH:/mnt/hgfs/CS2406F/2024-1-15Linux基础/code
export PATH=/mnt/hgfs/文件共享/笔记-代码/@数据结构/7-查找算法
echo $PATH/mnt/hgfs/文件共享/笔记-代码/@数据结构/7-查找算法
如果是在终端输入,只对当前终端有效,终端关闭之后,环境变量的值就失效了
如果你想要环境变量永久有效,直接写入到系统的配置文件:
1. ~/.bashrc
此配置只对当前用户有效
每一次打开终端都会执行这个脚本文件,只需要把export那一句加入到文件的后面就可以了2. /etc/profile
此配置对所有用户有效,每一次开机都会执行这个脚本文件,只需要把export那一句加入到文件的后面就可以了3. /etc/environment <--------
此配置对所有用户有效,只需要把export那一句加入到文件的后面就可以了如果觉得编译器的命令太长了,给arm-none-linux-gnueabi-gcc创建一个软链接
arm-none-linux-gnueabi-gcc
------->
arm-linux-gcc
ln -s arm-none-linux-gnueabi-gcc arm-linux-gcc
三、shell数组
同样不需要定义,可以直接使用,不能一次性赋值,等号右边都是字符串
用法:
数组名[下标]=value
下标: 0,1,2,3,4,5....n-1引用方式:
${数组名[下标]}
引用数组中所有的元素:
${数组名[*]}
${数组名[@]}
引用数组的第一个元素:
${数组名}#!/bin/bash a[7]={a,b,c,d,e,f,g} a[1]=a a[2]=b echo ${a[1]} #--->a echo ${a[2]} #--->b echo ${a[*]} #--->a b {a,b,c,d,e,f,g} echo ${a[@]} #--->a b {a,b,c,d,e,f,g} echo ${a} #--->没有输出
四、shell中的语句
1. 说明性语句
以#开头的行是说明性语句(注释),第一行除外2. 功能性语句
一般是指任意的操作系统shell命令(如:ls echo cd date ...),自己写的程序
上面几种可以直接运行,也可以使用反撇号获取结果shell内部的命令(read,expr,test....)
read: 在shell中表示从终端获取输入,保存到指定的变量
如:
read var1 var2
从终端获取两个变量(字符串),第一个字符串保存在var1中,第二个字符串保存在var2中输入----->键盘(标准输入)
输出----->终端/控制台(标准输出)
输入和输出可以重定向到指定的位置
1) 输入重定向
命令的输入请求通常是向标准输入设备(键盘...)提交请求
我们也可以很方便的把输入重定向到一个文件,这种情况叫做输入重定向
方法:
在命令后面添加 < filename
该命令的所有输入都来自于filename指定的文件2) 输出重定向
命令(程序)的输出通常是提交到标准输出文件(终端/控制台)
我们也可以很方便的把输出重定向到一个文件,这种情况叫做输出重定向
方法:
在命令的后面添加 > filename
or
在命令的后面添加 >> filename>: 会清空原先1.txt的内容,然后把命令的输出内容写入到1.txt文件
>>: 追加输出,不会清空原先1.txt的内容,把命令的输出内容写入到1.txt文件的后面find ./ -name 1.c > 1.txt
find ./ -name 1.c >> 1.txt
./1 > 1.txt
3) 命令中的管道用法
|
用法:
命令1 | 命令2
命令1的输出内容作为命令2的输入
如:
grep -nar main 查找当前目录中所有文件有main的行
grep -nar int 查找当前目录中所有文件有int的行grep -nar main | grep int
😭如:
awk -F: '{print $1}' /etc/passwd-F: 指定列的分隔符
$1: 是分隔符分割之后的第一个字段(列)
/etc/passwd 你要处理的文件名
'{print $1}' 你要执行的动作awk -F: '{print $1 $3}' /etc/passwd | grep china
在所有用户中查找名字叫做china的用户的UIDexpr: 执行算术运算符命令
expr主要用来进行简单的整数运算
+ - * / %
如:
expr 5 + 3 (运算符两边,前后至少一个空格)
计算5+3的结果输出到标准输出expr 5+3 ---->输出5+3
expr 5 - 3
expr 5 \* 3 *在shell中是一个通配符
expr 5 / 3 --->输出1
expr 5 % 3也可以把计算之后的结果赋值给指定的变量
ret=`expr 5 + 3`
i=6
ret=`expr $i + 5 \* 3`test: test语句可以测试三种对象
字符串 整数 文件
a. 字符串测试
=测试两边的字符串内容是否完全相同
=的两边至少要有一个空格
如:
test "abc" = "abc"
echo $? 获取上一次命令的结果结果:
相等结果为0
不相等结果为1可以比较两个变量是否相同
str1=abc
str2=abc
test $str1 = $str2
echo $?如果比较的时候,有一个字符串为空,就会出现语法错误
str1=abc
str2=
test $str1 = $str2 #test "abc" = 报错
echo $?小技巧:
str1=abc
str2=
test ${str1}a = ${str2}a
echo $?!= 测试两个字符串的内容是否不相同
不相同返回0,相同返回1
test "abc" != "abc"
echo $?-z zero测试字符串是否为空
为空就返回0
test -z ${str2}echo $?
-n not null 测试字符串是否不为空
不为空返回0
test -n "abc"
echo $? #0
总结:符合你描述的条件,返回0,不符合你描述的条件,就返回1
b. 整数测试
#-eq equal 测试两个整数是否相等,相等返回0
test 3 -eq 4
echo -eq:$? #1var1=123
var2=123
test $var1 -eq $var2
echo -eq:$? #0#-ne not equal 测试两个整数是否不相等,不相等返回0
test 3 -ne 4
echo -ne:$? #0
test $var1 -ne $var2
echo -ne:$? #1#-gt greate than 测试一个整数是否大于另一个整数
test 3 -gt 4
echo -gt:$? #1var1=123
var2=12
test $var1 -gt $var2
echo -gt:$? #0#-ge greate and equal 测试一个整数是否大于等于另一个整数
test 3 -ge 4
echo -ge:$? #1var1=123
var2=123
test $var1 -ge $var2
echo -ge:$? #0
#-lt little than 测试一个整数是否小于另一个整数
test 3 -lt 4
echo -lt:$? #0var1=123
var2=12
test $var1 -lt $var2
echo -lt:$? #1#-le little and equal 测试一个整数是否小于等于另一个整数
test 3 -le 4
echo -le:$? #0var1=123
var2=12
test $var1 -le $var2
echo -le:$? #1c. 文件测试
-d filename 测试filename是否为一个目录文件(directory)
-f filename 测试filename是否为一个普通文件
-L filename (大写)测试filename是否为一个链接文件(硬链接是一个普通文件)
-p filename 测试filename是否为一个管道文件
-s filename 测试filename是否为一个套接字(socket)文件
-c filename 测试filename是否为一个字符设备文件
-b filename 测试filename是否为一个块设备文件test -d $1
echo $?
-r filename 测试filename是否存在并且可读
-w filename 测试filename是否存在并且可写
-x filename 测试filename是否存在并且可执行
test -r filename
echo $?test file1 -nt file2 newer than
测试file1是否修改时间在file2的后面(测试file1是否比file2新)
test file1 -ot file2 old than
测试file1是否修改时间在file2的前面(测试file1是否比file2旧)test命令可以使用[]来缩写
test Expression <=========> [ Expression ]
如:
[ $str1 = $str2 ]
echo $?
[ 和 ] 两边都必须有空格test的复合表达式
相当于C语言的逻辑表达式(&& ||)
组合了两个或者两个以上的表达式叫做复合表达式
使用方式:
a. 可以使用test内置的操作符来创建复合表达式
test Expression1 "操作符" Expression2
操作符:
-a and 并且
全部满足才返回0
-o or 或者
任意一个满足就返回0
如:
测试..是否为目录的同时,测试位置变量$1和$2是否相等
test -d ".." -a $1 -eq $2
echo $?
===========>
[ -d ".." -a $1 -eq $2 ]
echo $?b. 使用条件操作符来表示复合表达式
test expression1 "条件操作符" test expression2
条件操作符(&& ||)
&& 并且
|| 或者
test -d ".." && test $1 -eq $2
echo $?
===========>
[ -d ".." -a ] && [ $1 -eq $2 ]
echo $?3. 结构性语句(分支语句 / 循环语句)
a. 分支(条件)语句
相当于C语言中的if语句,不可以使用else if注意:if后面的条件成立则执行if后面的语句(表达式返回0表示成立,和C语言不一样)
语法:
if command; then
语句列表
fi #表示if语句结束
--------------------------
if command; then
语句列表1
else #可以不要
语句列表2
fi #表示if语句结束如:
read var1
if [ `expr $var1 % 2` -eq 0 ]
then
echo 偶数! # <===> echo "偶数!"
else
echo 奇数!
fi
b. 多路分支语句(类似于C语言中的switch)
语法:
case 变量 in
模式1)
...
;; #类似于C语言中的break,但是在shell中;;不能省略
模式2)
...
;;
模式n)
...
;;
esac #表示case语句结束#!/bin/bash read var case $var in a) echo var is a ;; b) echo var is b ;; c) echo var is c ;; esac
case语句还有一个强大的功能在于可以使用"模式/规则"进行匹配而不是固定的字符串来匹配
一个模式是由一串正常的字符串和特殊的通配符组成的字符串。该模式可以使用正则表达式(有限支持)
* shell通配符,在shell中表示任意多个任意字符(可以是0个)
? shell通配符,在shell中表示任意一个字符输入一个文件名,判断这个文件是什么类型的?
脚本文件
头文件
c语言文件
C++文件
#!/bin/bash filename=$1 case $filename in *.sh) echo "脚本文件" ;; *.h) echo "头文件" ;; *.c) echo "c语言文件" ;; *.cpp) echo "C++文件" ;; esac
c. 循环语句
for循环
语法:
for 变量名 in 单词表(列表)
do
循环体语句
done
单词表: 以空格隔开的字符串列表
如:
ls输出的结果
a b c d e f g h
shell中的数组
....
for循环的循环次数就是"单词表"中的单词的个数,并且每次执行的时候,变量的值就是取单词表中的一个单词for var in a b c d e f g do echo $var echo ------- done for var in `ls` do echo $var echo ------- done
for也可以写成C语言的风格
如: 计算1~100的和#!/bin/bash sum=0 for((i=0;i<=100;i++)) #C语言风格的for循环 do sum=`expr $sum + $i` done echo sum=$sum sum=0 for ((i = 0; i <= 100; i++)) #C语言风格的for循环 do sum=`expr $sum + $i` done echo sum=$sum
while循环
语法:
while 命令或者表达式
do
循环体语句
done
命令或者表达式经常是test取测试一个条件sum=0 i=0 while [ $i -le 100 ] do sum=`expr $sum + $i` i=`expr $i + 1` done echo sum=$sum
sum=0 i=0 while ((i <= 100)) do sum=`expr $sum + $i` i=`expr $i + 1` done echo sum=$sum
练习:
1. 使用shell,求出1000以内所有的水仙花数
#!/bin/bash for ((i = 100; i < 1000; i++)) #C语言风格的for循环 do a=`expr $i % 10` b=`expr $i / 10 % 10` c=`expr $i / 100` sum=`expr $a \* $a \* $a + $b \* $b \* $b + $c \* $c \* $c` if [ $sum -eq $i ] then echo $i fi done
2. 写一个脚本,统计一个指定文件夹中有多少个普通文件?有多少个目录文件?
#!/bin/bash # $#表示命令行参数的个数(不包括脚本的名称) # -ne not equal 测试两个整数是否不相等,不相等返回0 if [ $# -ne 1 ] then echo "arg num error" fi file=0 dir=0 cd $1 #把工作路径切换为指定路径 # pwd=`pwd` for var in `ls -a $1` #列举指定目录中的文件(得到的仅仅是名字列表,不带路径) do # echo $pwd/$var if [ -f $var ] #判断工作路径下是否有这个普通文件 then file=`expr $file + 1` fi if [ -d $var ] then dir=`expr $dir + 1` fi done cd - #把工作路径切换回来 echo file=$file echo dir=$dir
3. 写一个脚本,计算一个指定的文件有多少行?
read 可以一次读一行
#!/bin/bash if [ $# -ne 1 ] then echo "arg num error" fi line=0 while read var #读取一行 do line=`expr $line + 1` echo $var done < $1 #整个while内部的输入都来自于$1 #写法叫做代码块重定向,也就是把一组命令同时重定向到一个文件 echo "line = $line"
4. 写一个简单的脚本,实现文件拷贝的效果(不使用cp命令)
把第一个文件的每一行内容读出来,写入另一个文件
read:读
echo 重定向
#!/bin/bash if [ -f $2 ]; then rm -rf $2 fi while read var do echo $var >> $2 done < $1
until 循环
语法:
until 条件(命令/表达式)
do
循环体语句
doneuntil和while语句的功能类似,所不同的是,while只有当测试条件满足的时候,进入循环,而until只有当测试条件不满足的时候,才进入循环,条件满足则退出循环,这一点刚好和while相反
#!/bin/bash sum=0 i=0 until [ $i -gt 100 ] do sum=`expr $sum + $i` i=`expr $i + 1` done echo until_sum=$sum sum=0 i=0 until ((i > 100)) do sum=`expr $sum + $i` i=`expr $i + 1` done echo until2_sum=$sum
d. break和continue
break n
跳出第n层循环(c语言中只能跳出当前循环)
continue n
转到第n层循环语句开始执行下一次循环
数字n表示,第几层循环
break和continue后面也可以不加n,不加n表示的含义和C语言一样#!/bin/bash for ((i = 0; i < 10; i++)) # 10次 do for ((j = 0; j < 10; j++)) # 10次 do echo $i $j continue 2 done echo "nihao" done echo hello #执行结果 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 hello
#!/bin/bash for ((i = 0; i < 10; i++)) # 10次 do for ((j = 0; j < 10; j++)) # 10次 do echo $i $j break 2 done echo "nihao" done echo hello #执行结果 0 0 hello
五、shell中的函数
自定义函数的语法:
function_name() {
函数内部的命令列表
}function_name: 你自定义的函数的函数名,名字的取法和C语言类似(标识符)
函数不要写参数,如果有参数就是使用"位置变量"函数定义之后不会自动执行,需要调用才会执行
函数的调用语法:
function_name arg1 arg2 arg3 ....
在函数的内部
arg1 ------> $1
arg2 ------> $2
....函数的返回值的获取,通常有两种方式:
1. 通过反撇号获取命令/函数的执行结果
ret=`function_name arg1 arg2`
反撇号是获取函数function_name执行时输出到标准输出的内容
把原本输出到终端的所有的输出都赋值给变量ret#!/bin/bash sum() { s=`expr $1 + $2` echo $s echo nihao } echo start ret=`sum 123 456` echo stop echo $ret #执行结果 start stop 579 nihao
2. 使用位置变量 $?
$?表示的是上一条命令或者程序的退出码(函数的返回值)sum 123 132
结果是sum最后一个命令或者表达式的值
(在函数里面使用return返回你要返回的值)数字不能超过255#!/bin/bash sum() { s=`expr $1 + $2` echo $s return $s } echo start sum 123 132 echo $? echo stop #执行结果 start 255 255 stop
六、练习
1. 写一个函数,判断一个通过命令行传入的整数是否为质数
#!/bin/bash a=`expr $1 / 2` for ((i = 2; i <= $a; i++)) do num=`expr $1 % $i` if [ $num -eq 0 ] then break fi done if [ $i -gt $a ] then echo YES else echo NO fi
2. 写一个shell脚本,打印n以内的所有质数(n通过命令行传入)
#!/bin/bash for ((i = 2; i <= $1; i++)) do a=`expr $i / 2` for ((j = 2; j <= $a; j++)) do num=`expr $i % $j` if [ $num -eq 0 ] then break fi done if [ $j -gt $a ] then echo $i fi done