在ubuntu20.04下学习shell

这篇博客详细介绍了Shell脚本的基础知识,包括如何创建和执行脚本,赋予文件执行权限,以及使用vi/vim编辑器。内容涵盖了变量、环境变量、内部变量的使用,数组操作,数学运算,流程控制语句(if、case、循环),函数定义与调用,以及命令的组合执行。此外,还讲解了输入输出重定向和管道符的应用,以及shell脚本中的通配符和别名设置。
摘要由CSDN通过智能技术生成

记录学习shell时笔记,整理一下方便以后忘记了回来学习


学习shell

第一个shell文件:打印hello world!~

其实在vi+文件名之前可以有另一个命令:touch+文件名
因为vi+文件名表示打开该文件(如果没有就自动创建一个文件)
而touch+文件名才表示创建一个新的文件

  • 1.在目标文件夹里打开终端
  • 2.输入 vi/vim 新建的文件名
  • 3.输入 i(表示在第一行开始插入数据)
  • 4.输入目标代码
  • 5.按下 ESC
  • 6.然后输入 :wq(表示保存输入并退出)
  • 7.输入 sudo chomd +x 文件名(给予文件可执行权限,可以通过ll+文件名来查看,文件名后连×的就是可执行文件)
  • 8.运行文件,输入 ./文件名
vi/vim hello	//创建一个叫hello的文件
#! /bin/bash	//无实际意义,告诉我们这是用bash终端
echo "hello world~"/hello world!~	//两种方式都可以,可以不加单双引号,echo就是输出的意思
sudo chmod +x hello
./hello
第二个shell文件,添加其他命令
  • 1.进入文件 vi/vim 目标文件
  • 2.输入命令并保存退出
  • 3.不用再次给予权限,直接运行 ./文件名

例如:在hello文件中加入其他命令

vi/vim hello
echo "hello world"
echo -------------
cd /	//切换到根目录
ls	//根目录下有那些文件
echo -----------------
date	//打印日期
./hello	//运行文件

基础知识

shell可以支持三种变量

1.自定义变量
1.1 变量名=值
haha=1
echo $haha

1.1.1变量命名的规则

  • 1.1.1.1 首字母必须是大写字母或者是小写字母
  • 1.1.1.2 不能有空格,可以用_(下划线)
  • 1.1.1.3 不能有标点符号
  • 1.1.1.4 不能使用shell中的保留关键字,可以用help命令查询保留关键字
1.2 把一个变量赋值给另一个变量
a='hello'	//注意等号两边不能有空格
b=$a
echo $b
name=jack	//这里可以不加单引号
message=他的名字叫${name}!	//如果最后没有内容(这里是!),可以不用大括号{}
echo $message
1.3 定义只读变量
haha=1
echo $haha
readonly haha
1.4 删除变量:只能删除普通变量,不能删除只读变量
hehe=2
echo $hehe
unset hehe	//删除变量hehe
echo $hehe
2.环境变量:例如($PATH),可视作全局变量
2.1 临时添加环境变量:只在当前终端有效,该终端关闭之后,变量就失效

export 变量名=变量值

export lala=1	//重启一个终端就无效啦
echo $lala

环境变量和普通变量的区别

export a=1
echo $a
b=2
echo $b
bash	//在该终端下开启子终端,终端里的终端
echo $b	//不会再显示了,普通变量不能在子终端中使用
echo $a
exit	//退出子终端
echo $b
unset a	//删除环境变量
echo $a
2.2 永久添加环境变量
2.2.1 方案一:添加完仅对当前用户有效,切换到其他用户,其他用户不能使用这个环境变量
通过修改~/.bashrc文件,在最后一行上添加上export 变量名=变量值,然后保存退出
 两种生效方法:
	1.关闭当前终端,重新打开一个新终端窗口就能生效
	2.输入"source~./bashrc"命令,立即生效
有效期限:永久生效
用户局限:仅对当前用户
vi .bashrc	//在主目录中打开
export haha=123456789	//找到最后一行,然后点击i开始插入所示代码,然后ESC,然后输入:wq保存退出
source ./bashrc	//让文件生效生效

多个终端打开可以输入然后看到123456789

echo $haha
2.2.2 方案二
通过修改/etc/profile文件
sudo vi /etc/profile
export haha=1

生效方法:系统重启
有效期限:永久有效
用户局限:对所有用户
sudo vi /etc/propile
export haha=1213131	//在文档最后一行输入,i表示插入,然后保存退出(同上)
echo $haha	//重启之后就可以使用在所有终端了
3.内部变量/位置参数/命令行参数:linux提供的特殊类型变量,例如内部变量$$储存李当前进程进程号
3.1  $0:编译脚本的命令行
3.2  $n:传递给脚本或函数的参数,n是数字,$1表示第一个参数,以此类推
 当参数超过10个,用{}表示,例如${12}
3.3  $#:传递给脚本或函数的参数个数
3.4  $*:传递给脚本或函数的所有参数(所有参数可看作一个整体的字符串)
3.5  $@:传递给脚本或函数的所有参数(每个参数都是单独的字符)
3.6  $?:函数的返回值,或成为上个命令的退出状态(大部分命令执行成功时退出状态为0,失败为1)
3.7  $$:当前shell进程号
vi haha
#! /bin/bash
echo 文件名:$0	//可加引号,可不加
echo 第一个参数:$1
echo 第二个参数:$2
echo 所有参数:$*
echo 所有参数:$@
echo 参数个数:$#
echo 当前进程ID:$$
bash haha 11 22 33	//haha是指文件名,后面三个是赋的值
vi haha
#! /bin/bash
set 11 22 33
echo 文件名:$0	//可加引号,可不加
echo 第一个参数:$1
echo 第二个参数:$2
echo 所有参数:$*
echo 所有参数:$@
echo 参数个数:$#
echo 当前进程ID:$$
4 变量值输出
4.1 命令echo
str="您好~" 
echo $str

与字符混用

str="您好!"
echo "$str,这是一个测试"

用{}与字符混用

month=9
echo "2021-${month}-10"

使用转义符\,输出特殊字符

str="您好"
echo "$str\",\$"
4.2 命令printf

与echo类似

printf"您好!\n"
可以带参数,和C语言,python用法相同,%s表示字符串,%d表示整数
printf "我是一名%s,今年%d岁\n" "学生" "30"

4.3 从用户输入获取变量值
使用read命令,类似与python中input
创建一个文件,展示read写法
vi test
#! /bin/bash
read -p "请输入两个数字" d1 d2
echo "输入的第一个数字是$d1"
echo "输入的第二个数字是$d2"
bash test
4.3 变量替换
4.3.1  ${haha}:正常使用变量haha
4.3.2  ${haha:-$hehe}:如果变量haha为空,返回变量hehe的值,不改变haha的 值
4.3.3  ${haha:=$hehe}:如果变量haha为空,返回变量hehe的值,将hehe的值赋值给haha
4.3.4  ${haha:?$hehe}:如果变量haha为空,将hehe发送到标准错误输出(终端)
		用来检测haha是否被赋值,未被正常赋值,脚本运行将暂停报错
4.3.5  ${haha:+$hehe}:如果变量haha被定义,返回变量hehe的值,不改变haha的值
5. 数组
bash仅支持一维数组,下标也是从0开始,数组元素用空格分开
定义数组:数组名=(值1 值2 ....值n)
shuzu=(A B C D)
echo ${shuzu[0]}	//取数组中的第一个值
echo ${shuzu[*]}	//取数组中的所有值
echo ${shuzu[@]}	//取数组中的所有值
echo ${#shuzu[@]}	//数组的元素个数
echo ${#shuzu[1]}	//数组中第二个元素的长度

6 数学运算
6.1 整数加减乘除
6.1.1 直接计算
echo $[1+1]
echo $[9-1]
echo $[9*3]
echo $[9/8]	//这个答案为1
6.1.2 计算并给变量赋值
a=$[1+1]
b=$[3-2]
c=$[7*1]
d=$[8/7]
echo $a $b $c $d
6.1.3 变量间的计算赋值
直接相加获得字符串
a=1
b=2
c=$a+$b
echo $c
使用let命令,获得数字结果
a=1
b=1
let.c=$a+$b
echo $c
使用双括号(())获得数字结果
a=1
b=2
((c=a+b))
echo $c
6.2 浮点数(小数)加减乘除
6.2.1 加减
echo 1.1+2.2|bc
echo 2.1-2|bc	//小于1的小数之前的0被省略
printf %.2f `echo 2-1.3|bc	//保留两位小数,注意的是,这个不单引号,而是ESC下面那个键`
6.2.2 乘
其中scale是控制保留小数点位数的
echo "1.1*1.1"|bc
echo "scale=2;1.1*1.1"|bc	//小数点保留的位数为2
6.2.3 除
其中scale是控制保留小数点位数的
echo "4/3"|bc
echo "scale=3;4/3"|bc
6.3 计算并给变量赋值
c=$(echo "1.1*1.1"|bc)
echo $c

c=$(echo "scale=2;1.1*1.1"|bc)
echo $c
6.4 变量间计算赋值
a=1.1
b=1.2
x=$(echo "scale=3;$a*$b"|bc)
echo $x
7 流程控制语句
7.1 条件语句
7.1.1 if判断

if语句的类型

 if...fi结构
	如果判断条件为真,就执行then后面的语句
	
	if [判断条件]		 //if和判断条件之间有一个空格,判断语句和[]之间也有空格
	then
		程序语句
	fi
vi test
a=3
b=3

echo a等于$a
echo b等于$

if [ $a == $b ]
then
    echo a等于b
fi

判断条件(逻辑表达式)

判断条件介绍
用[ 判断条件 ]或者 test 判断条件,test 不常用
例:[ $a == b ] 中 b ]中 b]a和$b的两侧必须有空格

整数条件运算符

相等:-eq	例:[ $a -eq $b ] 或 [ $a == $b ]
不等于:-ne	例:[ $a -ne $b ] 或 [ $a != $b ]
大于:-gt	例:[ $a -gt $b ]
小于:-lt	例:[ $a -lt $b ]
小于等于:-ge	例:[ $a -ge $b ]
大于等于:-le	例:[ $a -le $b ]

字符串关系运算符

相等:= 	例:[ $a = $b ]
不想等:!= 	例:[ $a != $b ]
检测字符串长度是否为0:-z	例:[ -z $a ]
检测字符串长度是否不为0:-n	例:[ -n $a ]
检测字符串长度是否为空:直接放入字符串变量	例:[ $a ]

文件测试运算符

-d 检测文件是否是目录	例:f="/home/q.text"	[ -d $f ]
-f 检测文件是否是普通文件	例:[ -f $f ]
-r 检测文件是否可读	例:[ -r $f ]
-w 检测文件是否可写	例:[ -w $f ]
-x 检测文件是否可执行	例:[ -x $f ]
-s 检测文件大小是否大于0	例:[ -s $f ]
-e 检测文件是否存在	例:[ -e $f ]

布尔运算符

与:-a 	例:[ $a==$b -a $c==$d ]
或:-o 	例:[ $a == $b -o $c ==$d ]
非:! 	 	例:a=false 或 a=true [!$a ]

if...else...if结构
如果判断条件为真,就执行语句1
否则,执行语句2
if [ 判断条件 ]
then
	程序语句1
else
	程序语句2
fi
vi tst	
a=$1	//在这里其实有一个$0,但是$0输出得到的是该文件的名字,在这里无意义
b=$2

echo a等于$1
echo b等与$2

if [ $1 == $2 ]
then 
    echo a等于b
else
    echo a不等于b
fi
if...elif...if结构
如果判断条件1为真,就执行语句1,然后结束
如果判断条件1为假,进行判断条件2的判断,为真就执行语句2
否则,执行语句3
if [ 判断条件1 ]
then
	程序语句1
elif [ 判断条件2 ]
then
	程序语句2
else
	程序语句3
fi
a=$1
b=$2

echo a等于$1
echo b等于$2

if [ $a==$b ]
then
    echo a等于b
elif [ $a -gt $b] 
then
    echo a小于b
else
    echo a大于b
fi
if结构可以嵌套,一个If结构可以包含另一个if结构
if结构中的elif和else都是可选的,不必需有
7.1.2 case判断
case是一种多选择结构,可以匹配值,如果匹配成功,执行对应的语句,执行后结束,不执行其他语句

case 值 in
值1)
	语句1
	;;
值2)
	语句2
	;;
值3)
	语句3
	;;
×)
	其他语句
esac
echo 1:榴莲
echo 2:椰子
echo 3:水蜜桃
echo 4:哈密瓜
echo 5:芒果
read -p “请选择你喜欢的水果:fruit	//这里需要在变量之前有一个空格,或者直接把变量之前的字符用双引号
case $fruit in
1)
	echo 你喜欢榴莲
	;;
2)
	echo 你喜欢椰子
	;;
3)
	echo 你喜欢水蜜桃
	;;
4)
	echo 你喜欢哈密瓜
	;;
5) 
	echo 你喜欢芒果
	;;
*)
	echo 你可能不喜欢水果
esac	

	
7.2 循环结构
7.2.1 while循环
while 判断条件
do
	语句
done

当判断条件为真时,循环执行语句,直到判断条件为假时,跳出循环
a=0
read -p 请输入一个数字:  num

while [ $a -lt $num ]
do
	echo $a
	((a=a+1))
done
cat tes	//可以看见文件中的代码
7.2.2 until循环
do
	语句
done

until 执行循环语句,直到until判断条件为真时结束
a=0
read -p 请输入一个数字: num

until [ $a == $num ]
do
	echo $a
	((a=a+1))
done

clear可以清除终端中的命令

5.2.3  for循环

for 变量 in 变量
do 
	语句
done

循环次数等于列表中元素个数,每次循环,变量值等于当前列表中对应的元素
for x in 1 2 3 4 5 6 7 8 9
do
	echo $x
done
7.2.4 跳出循环
break
break 用来终止一个循环
break n ,其中n表示跳出循环的几层循环,默认是1
for x in 1 2 3 4 5 6
do 
	echo $x
	if [ $x == 2 ]
	then
		break
	fi
done
for x in 1 2 3
do
	for y in 3 4 5
	do
		if [ $x == $y ]
		then 
			echo x=$x,y=$y,x=y,跳出循环
			break 2
		else
			echo x=$x,y=$y,x!=y,循环继续
		fi
	done
done
continue
continue 用来跳过本次循环,直接进行下一次循环
continue n  ,其中n表示跳出的几层循环,默认是1
for x in 1 2 3 4 5
do
	if [ $x == 3 ]
	then 
		continue
	fi
	echo x=$x
done
exit
exit n, 退出当前终端,并设置退出值为n,不设置n默认为0
可使用$?查看最后一个命令的退出值
echo hello
exit 123
echo hello
echo  $?
7.3 多命令组合执行
7.3.1 &&

用法:命令1 && 命令2
含义:顺序执行命令1,命令2,但只有在命令1执行成功的情况下才执行命令2

cd / && l
// 首先进入根目录,然后查询有什么文件
rm qqq && l	//rm+文件名表示删除该文件
// 删除一个不存在的文件然后查询当前文件夹下有什么文件
7.3.2 ||

用法:命令1 || 命令2
含义:顺序执行命令1,命令2,但只有在命令1执行失败的情况下才执行命令2

cd / || l
//命令1进入根目录,命令2查询当前目录有什么文件
cd haha || mkdir haha
//进入一个名叫haha的文件夹,当文件夹不存在(即命令1执行失败),创建一个名叫haha的文件夹
7.3.3 &&和|| 联合使用

用法:命令1 && 命令2 || 命令3
含义:从左往右看先看命令1和命令2,执行结果作为一个整体,可以这么理解(命令1 && 命令2)|| 命令3,括号里面两个命令都执行成功视为成功,则不执行命令3,有一个失败就视为失败,执行命令3

cd / && l || echo "命令1或命令2执行失败"

用法:命令1 || 命令2 && 命令3
含义:从左往右看先看命令1和命令2,执行结果作为一个整体,可以这么理解(命令1 || 命令2)&& 命令3,括号里面两个命令有一个执行成功视为成功,则不执行命令3,都失败就视为失败,执行命令3

cd / || l && echo "命令1或命令2执行成功"
7.3.4 使用括号()的组合命令

命令1 || (命令2 && 命令3)
命令1失败,执行(命令2 && 命令3),(命令2 && 命令3)看成整体

echo 1 || (echo 2 && echo 3)

8 函数
8.1 函数定义
函数名()
{
	函数内容
	return 返回值
}

函数的返回值只能是整数,如果不加return返回值,将会把最后一条命令的退出值作为返回值
8.2 函数调用

函数名 参数1 参数2 …

8.3 实例
8.3.1 调用函数,打印hello world
touch hello
vi hello
hello()
{
	echo "hello world!~"
}
hello	//要想函数被调用,这里要写上文件名
bash hello或者./hello
8.3.2 在函数外部如何使用函数中的变量
shell 中的函数被调用后,函数外部可直接使用内部定义的变量
hello()
{
	haha="hello world!"
}
hello
echo $haha
8.3.3 获取函数的输出值赋值给变量
hello()
{
	echo "hello world!"
}
haha=$(hello)
echo $haha
8.3.4 带返回值的函数
参数传递使用 $1  $2等表示,返回值用$?表示
love()
{
	((haha=$1+$2+$3))
	echo 第一个参数为 $1
	echo 第二个参数为 $2
	echo 第三个参数为 $3
	return $haha
}

love 3 6 4
echo love函数的返回值为$?
一些内置命令

read是shell内置命令,用于从标准输入中读取数据并赋值给变量,如果没有进行重定向,默认就是从终端控制台读取用户输入的数据,如果进行了重定向,那么可以从文件中读取数据.

语法:read [options] [var1 var2]
options表示选项,如下所示,var表示用来存储数据的变量,可以是一个,也可以是多个
-n num 读取num个字符,而不是整行字符
-p prompt 显示提示信息,提示内容为prompt
-s 静默模式,不会再屏幕上显示输入的字符
-t seconds 设置超时时间,单位为秒,如果用户没有在指定时间内输入完成,那么read将会返回一个非0的退出状态,表示读取失败.

cat 文件名		//显示代码内容
#! /bin/bash			//说明该脚本是用哪一种shell编写的,通常放在脚本的第一行,从而调用相应的解释程序予以执行
#ShowHello
#To show hello to somebody

echo -n "Enter Your Name"	//参数-n的作用是不换行,echo默认换行
read name			//从键盘输入信息
echo "Hell0 $name"

Linux 系统中最常见的shell类型为Bourne shell(简称sh),C shell(简称csh),Korn shell(ksh),Bourne-again shell(简称bash)

1.命令记忆功能	./bash_history
2.自动补全功能	Tab键是个好东西
3.别名设置功能	alias ll='ls -l'	左边是新的名字,右边是之前的名字或者属性
通配符
 *
*可以任意字符的0次或者多次出现
例如:

用户输入 ls -l file* ,在这个命令中,使用了通配符,shell在执行命令之前,会以合适的文件名进行替换,找出当前目录中以file开头的文件名并执行。所以,当shell使用通配符时,在参数传递给程序之前,通配符已经变成实际文件名并执行。所以,当shell使用通配符时,在参数传递给程序之前,通配符已经变成实际文件名,如果通配符不能匹配任何文件,shell将会显示一个报错信息。
* 可以匹配/(斜线)字符之外的任何字符,因为/用作路径名中的定界符


?
? 匹配任意单个字符(除/之外)
d? 表示以‘d’开头的两个字符的文件名
??  表示任意两个字符的文件名
?*y 表示至少两个字符,并且以“y”结束的文件名

[] 一对方括号

[] 将一组字符列表括起来,其作用是匹配该字符组所限定的任何一个字符,即指定列表中的任意一个字符。
例如:space.[co]匹配space.o或者space.c
[Hh]* 匹配以H或者h开头的文件名
[] 中无论有几个字符,都只代表一个字符,在[]中,也可以用-指定字符的范围,例如[1-9]

{} 大括号
{string1,string2 ,sring3...}匹配string1或者其他的字符串
但这种写法只在bash,tcsh和C sh


输出的时候有一个顺序执行,需要在各个命令之间加上;
例如:
ls;date;pwd;cd /user
输入输出重定向符
标准输入重定向:
command <fle	//将文件作为命令输入
//例如:bash < sho1的意思是让bash执行时,直接从sh01中读取数据,而不必交互式地从键盘上输入
//wc -l <file  表示统计file文件中有多少行文本
command <<分界符	//从标准输入中读入,直到遇见分界符才停止

//
标准错误输出重定向:
command 2>file	//以覆盖的形式,把command中的错误信息输出到file文件中
command 2>>file	//以追加的形式,把command的错误信息输出到file文件中
标准输出重定向:
command >file	//以覆盖的形式,把command中的正确结果输出到file文件中
command >>file	//以追加的形式,把command的正确信息输出到file文件中
管道符
在shell中,管道实际上就是进程之间的一个通信工具,那么用在Linux命令中主要是方便两条命令互相之间可以相互通信。

管道符是"|",主要是把两个应用程序连接在一起,然后把第一个应用程序的输出,作为第二个应用程序的输入。如果还有第三个应用程序的话,可以把第二个程序的输出,作为第三个应用程序的输入,以此类推。

如 [ ls | grep test.s ],在当前文件过滤出test.sh文件:
[root@lincoding /]# ls 
bin   data  etc   lib    lost+found  net   opt   root  selinux  sys    usr
boot  dev   home  lib64  media       mnt   proc  sbin  srv   test.sh   tmp
[root@lincoding /]# ls | grep test.sh 
test.sh
管道符"|"就把ls命令的文件列表输出给到了[grep test.sh]命令来过滤文件。


管道符还有一个需要注意的地方,我们可以通过下面的命令观察到,在使用管道符的时候,管道符会为两条命令产生了子进程
[root@lincoding /]# cat | ps -f
UID         PID   PPID  C STIME TTY          TIME CMD
root       2627   2623  0 14:57 pts/0    00:00:00 -bash
root      88029   2627  0 19:51 pts/0    00:00:00 cat
root      88030   2627  0 19:51 pts/0    00:00:00 ps -f
父进程bash的pid为2627,子进程cat的pid为88029,子进程ps -f的pid为88030。

由于管道符是会为连接的命令产生子进程,所以也是不会影响当前环境的。

我们用cd /home/ | ls命令验证下,运行结果如下:
[root@lincoding /]# cd /home/ | ls 
bin   data  etc   lib    lost+found  net   opt   root  selinux  sys    usr
boot  dev   home  lib64  media       mnt   proc  sbin  srv   test.sh   tmp   
[root@lincoding /]# pwd
/
从以上的运行结果可以得知,类似切换目录cd这种会影响当前环境的命令,在使用了管道符之后,就没有对当前环境造成影响了。

如果使用分号";"连接两条命令会如何呢?
[root@lincoding /]# cd /sys/ ; ls
block  bus  class  dev  devices  firmware  fs  hypervisor  kernel  module  power
[root@lincoding sys]# pwd
/sys
可以得知,通过分号";"连接,cd命令会对当前环境造成影响。
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值