【Linux】Shell脚本

文章目录

Shell脚本

预备知识

在ubuntu下,/usr/bin/bash存放shell的bash脚本解释器,/usr/bin/bash本质上就是sh的长连接。

使用echo $SHELL可以查看存储脚本的路径的环境变量。

设置脚本文件vim ./mybash.sh先设置一个脚本文件,脚本文件第一行要使用!#/bin/bash表示shell的bash脚本解释器的位置,使用#/bin/sh也一样,目的是为了寻找到脚本解释器的位置。所谓的脚本,就是把多个shell命令,放到一个文件里一起执行。python也有对应的脚本解释器python.exe

执行脚本 :如果当前脚本文件有可执行权限,可以直接.mybash.sh执行。第二种方法:使用bash ./mybash.sh执行


变量

在开机后,系统会先从/etc/profile读取全局的系统环境变量信息,然后从~/.bashrc读取用户的系统变量信息,除了环境变量外,还包括命令行解释器、颜色、bash这是从这两个文件下读取配置信息完成初始化的,对于vim,在/etc/.vimrc文件下完成配置,使用set ts=4可以配置Tab键表示4个空格。修改后记得使用source + 指定配置文件完成重新加载。

变量作用域

export
  • Linux 环境变量章节也有讲解,作为内建命令,完成对父进程的环境变量的添加。这里添加的是临时的系统变量,只能在当前会话下有作用,可以交给子进程使用------所以说,如果此时执行bash脚本,可以看到export添加的系统变量,因为bash本来就是一个进程(使用ps命令可以查看bash的进程id,此外,查看时能看到查到的bash 应该名字是--bash,这是因为--bash表示父bash,也就是你当前的命令所处的bash进程,你使用bash test.sh执行脚本时,会添加一个新的bash进程,这个被称为子bash)。
  • 除了export可以设置系统变量外(注意了,只是临时的系统变量,只能在当前会话使用),可以通过修改/etc/profile~/.bashrc来永久添加系统变量,可以在所有会话内使用啦
i=10

如果只是以变量=值的格式设置变量,这个变量是临时的用户变量,只能在当前会话,且当前bash内有效,如果使用bash test.sh运行新的脚本,在新bash不能使用这个变量

readonly

使用readonly修饰变量表示只读,类似const,作用域参考上面两条。

特殊变量

$+数字

在学习cpp java时候我们都知道有一个东西叫做命令行参数,shell脚本也有这个东西,可以使用1表示传入的第一个参数,2表示第二个传入的参数

$#

表示传参个数,比如使用bash mybash.sh a b c d此时,echo $#打印4,echo $2打印b

$*

这个变量在for循环处使用样例来讲解

$@

这个变量在for循环处使用样例来讲解


运算符

A=3, B=8, 如果我们希望给D赋值A+B

  • D='expr $A+$B'。这里expr 表示计算,``可以将括住的内容作为一条完成的内容执行,不会截断,并且保留命令结果返回给D
  • D=$[A+B]或者D=$[$A+$B]
  • D=$((A+B))或者D=$(($A+$B))

综合运算

shell 复制代码
expr `expr 3+5` \* 5 # \*表示转义
echo $[(3+5)*5] #等价

条件判断

大小比较

-lt less than

-le less equal

-eq equal

-ne no equal

-gt greater than

-ge greater equal

字符串比较

=

权限判断

-r -w -x

文件类型

-f 是否是普通文件

-e 是否存在

-d 是否文件存在且文件是目录

if语句

语法格式

shell 复制代码
if [ 条件表达式 ];then #注意了,[右边有一个空格,]左边有一个空格
	程序
fi

# 或者
if [ 条件判断式 ]
	then
		程序
fi

使用样例

shell 复制代码
# 写一个判断人是否是少年、青年、老年的脚本,通过命令行传参
#!/bin/bash

if [ $1 -le 18 ];then
    echo "年龄是$1,少年"
elif [ $1 -gt 18 -a $1 -lt 40 ];then # -a表示并且,-o表或者,可以使用[[]]配合&& ||使用,表示 bash 扩展语法,支持 &&/||
    echo "年龄是$1,青年"
else
    echo "年龄是$1,中老年"
fi
~               

case

shell 复制代码
case $1 in
"start")
	echo "你写入start"
;; # 等价于其他语言的break
"end")
	echo "你写入end"
;;
*) # 等价于其他语言的default
	echo "其他"
;;
esac

for

shell 复制代码
#!/bin/bash

# 写一个1+...+100

sum=0
for i in {1..100};do # 这个i in {1.100}可以改为((i=1;i<=100;i++))
    sum=$[sum+i]
done

echo $sum  

# 此外,for in的功能十分强大,像是遍历ls结果也可以做到,注意的是,如果文件有空格,就会有问题,因为本质就是通过空格和换行符分隔给不同i
for i in `ls /`;do 
	echo $i
done

# 可以通过修改 IFS 变量临时改变分隔符,例如:
# 仅用换行符作为分隔符(忽略空格)
IFS=$'\n'  # 将IFS设为换行符,这里注意,\n和$'\n'不一样,
str="apple banana
cherry  date"

for fruit in $str; do
    echo "水果:$fruit"
done

unset IFS  # 恢复默认IFS
讲一下前文提到的\* @

代码

shell 复制代码
#!/bin/bash

echo "=== 1. \$* 遍历(不加引号) ==="
for arg in $*; do
    echo "参数:$arg"
done

echo -e "\n=== 2. \$@ 遍历(不加引号) ==="
for arg in $@; do
    echo "参数:$arg"
done

echo -e "\n=== 3. \"\$*\" 遍历(加引号) ==="
for arg in "$*"; do
    echo "参数:$arg"
done

echo -e "\n=== 4. \"\$@\" 遍历(加引号) ==="
for arg in "$@"; do
    echo "参数:$arg"
done

# 执行脚本
# bash ./test.sh "hello world" "foo bar" baz

执行结果

shell 复制代码
=== 1. $* 遍历(不加引号) ===
参数:hello
参数:world
参数:foo
参数:bar
参数:baz

=== 2. $@ 遍历(不加引号) ===
参数:hello
参数:world
参数:foo
参数:bar
参数:baz

=== 3. "$*" 遍历(加引号) ===
参数:hello world foo bar baz  # 所有参数合并成一个字符串

=== 4. "$@" 遍历(加引号) ===
参数:hello world
参数:foo bar
参数:baz  # 每个参数保持原始格式

关键分析 "$*"

  • 无论传入多少参数,"$*" 都会将它们拼接成一个字符串,拼接符是 IFS 的第一个字符(默认是空格)。例如上面的参数被拼接为 "hello world foo bar baz"

  • for arg in "$*" 遍历时,循环只会执行一次arg 变量的值是整个合并后的字符串(而非逐个参数)。

  • 仅当需要将所有参数作为一个整体处理时使用(例如作为单个字符串传递给其他命令),不适合遍历单个参数。

"$@"

  • 保留每个参数的原始格式(含空格的参数仍作为整体,推荐使用)。

while

shell 复制代码
i=1
sum=0
while [ $i -le 100 ];do
	let sum=sum+i
	let i++
done

补充let命令

let 是用于执行算术表达式的命令,可以直接进行整数运算(如加减乘除、自增自减等),并支持变量赋值。

支持基本算术运算与赋值,还有 多个表达式同时执行

shell 复制代码
a=2
b=3

let e=a*b # *不用转义
let "c=a+b, d=a*b"  # 同时计算 c 和 d
echo "c = $c, d = $d"  # 输出:c = 5, d = 6

*支持+±-和+= -= =


函数

basename

basename /usr/bin/a.out .out打印a,其中basename去除前缀,直到最后一个/ 后面的.out表示去除suffix(后缀)

dirname

dirname /home/shell/test01.sh 打印/home/shell 返回是文件所在的目录的绝对路径

实战

shell 复制代码
#!/bin/bash

# 写一个把"./"目录下所有后缀是.txt文件改为.sh后缀

dir = "./"
for i in `ls ${dir}*.txt`;do
    filename=`basename $i .txt`
    new_name="${filename}.sh"
    mv "$i" "$new_name"
done                     

自定义函数

注意的是,如果自定义函数想要return值,只能return 0~255的整数,结果通过echo $?获取

shell 复制代码
#!/bin/bash
function sum(){
	let s=$1+$2
	return $s
}
sum 100 200 # 这里超过了255,会发现返回值不是300.超过这个范围会被取模256.得44
echo $?

# 如果希望把更大的结果带出去
function sum2(){
	let s=$1+$2
	echo $s
}
result=`sum2 100 200`
echo result

实战

shell 复制代码
#!/bin/bash

# 计算阶乘

if [ $# -ne 1 ];then
    echo "请输入一个参数即可"
    exit -1;
fi

# 开始主逻辑
function jiecheng(){
    if [ $1 -le 1 ];then
        echo 1 # 当n=1时,返回1,``是把标准输出的结果传出去
        return 0
    elif [ $1 -gt 1 ];then
        let prev_n=$1-1
        tmp=`jiecheng $prev_n`
        let now=tmp*$1
        echo $now
        return 0
    fi
}

jiecheng $1   

工具

cut

shell 复制代码
cut -d ":" -f1-5 passwd
cut -d ":" -f1,2 passwd
# -d表示分隔符,后面接分隔符。-f表示列数,将指定列打印出来,passwd表示文件,可以使用管道进行读取其他结果

# 使用样例
grep "/bin/bash$" passwd | cut -d: -f1 # -d后面可以不加""

read

shell 复制代码
read -t 3 -p "请在三秒内输入一个大于1的数字: " num # -t 表示最多阻塞时间,-p表示显示提示信息

sed

对输入流(文件或管道数据)进行行级别的编辑(如替换、删除、插入、追加等),其特点是按行处理、默认不修改原文件(除非指定)。

shell 复制代码
# sed [选项] '命令' 输入文件
# -a add
# -d delete
# -s find and 替换
sed '1a hello world' seg.txt # 在第一行后添加hello world
# 注意,sed只是修改文件缓冲区,不修改文件,加-i选项才会修改文件
sed '/wo/d' seg.txt # 第一个/可以完成查找wo的工作,第二个/分割检索字段和操作字段
sed 's/wo/ni/g' seg.txt # s表示查找并且替换,wo表示谁需要被替换,ni表示替换成ni,g表示宣布替换(如果不添加g,每行只被替换一个)
sed -e '4d' -e 's/wo/ni/g' seg.txt # -e表示在指令生成文本修改,也就是可以同时执行多个指令

awk

shell 复制代码
# awk [选项参数] '模式{动作}' 输入文件
awk -F: '{print $1 "," $2}' passwd # -F表示切割,$1表示第一列,$2表示第二列
awk -F: '/^laoxiao/{print $1 "," $7}' passwd # ^ 是一个正则表达式元字符,表示 "行的开头"
awk -F: 'BEGIN{print "username, shell"}{print $1 "," $2}END{print "结束"}' passwd # begin表示在最开始先运行,end表示在最后运行

sort

shell 复制代码
# 默认以字符字典形式排序
# -n 数值大小拍
# -r 相反
# -t 指定分隔符
# -k 列数
相关推荐
qq_401700412 小时前
Linux 磁盘挂载管理
linux·运维·服务器
百***25613 小时前
Nginx作用以及应用场景
运维·nginx
小徐敲java3 小时前
window使用phpStudy在nginx部署前端测试
运维·前端·nginx
Crazy________3 小时前
38nginx四层负载均衡配置,和动静分离解析
linux·运维·nginx·负载均衡
YongCheng_Liang4 小时前
ELK 自动化部署脚本解析
linux·运维·elk·jenkins
小白博文4 小时前
MobaXterm调用远程服务器(Linux)图形化界面应用
linux·运维·服务器
百***67034 小时前
Nodemailer使用教程:在Node.js中发送电子邮件
linux·运维·node.js
TG:@yunlaoda360 云老大4 小时前
谷歌云发布 Document AI Workbench 最新功能:自定义文档拆分器实现复杂文档处理自动化
运维·人工智能·自动化·googlecloud
凄戚4 小时前
docker 镜像失效问题
运维·docker·容器