SHELL编程

Shell 是一种用户与操作系统内核进行交互的界面。它是命令行解释器,用户通过输入命令,Shell 解释并执行这些命令,从而操作系统中的各种功能得以实现。

  • 脚本需要以#!/bin/bash开头,并赋予执行权限
  • 脚本的执行方式:
    • 赋予脚本执行权限chmod u+x hello.sh,然后通过相对路径./hello.sh或通过绝对路径/home/shcode/hello.sh
    • 不赋予脚本执行权限,直接执行sh hello.sh

建议都直接加权限然后通过./运行

Q: 为什么有些命令通过sh hello.sh无法运行,然而通过chmod u+x hello.sh之后再./hello.sh就能运行起来呢?例如$[1+2]$

A: 在Unix/Linux系统中,当你使用 sh hello.sh 来运行脚本时:

  1. 使用的是 /bin/sh 解释器sh 通常指向 /bin/sh,这是一个兼容POSIX标准的Shell。在大多数现代Linux系统中,/bin/sh 是指向 bash 的符号链接,但它以POSIX兼容模式运行,禁用了 bash 的一些特性。
  2. 忽略脚本中的shebang行sh 命令会忽略脚本中的shebang行(#!/bin/bash),而是直接使用 /bin/sh 解释器。

当你通过 chmod u+x hello.sh 使脚本可执行,并使用 ./hello.sh 运行脚本时:

  1. 使用shebang行指定的解释器:如果脚本的第一行是 #!/bin/bash,它会使用 bash 解释器运行脚本。因此,脚本中的命令会按照 bash 的特性解释和执行。
  2. 脚本本身成为一个可执行文件:通过 chmod u+x,你使得脚本本身变成一个可执行文件,你可以直接通过路径来执行它。

假设 hello.sh 包含以下内容:

#!/bin/bash
echo $[1+2]
  1. 使用 sh hello.sh:

    • 由于 sh 以POSIX模式运行,$[1+2] 这种特性可能会被忽略或引发错误。
  2. 使用 chmod u+x hello.sh 之后 ./hello.sh:

    • 这时脚本会使用 #!/bin/bash 指定的 bash 解释器来运行,因此 echo $[1+2] 会正常输出 3

为了确保您的脚本在不同的运行方式下都能正常工作,可以使用更加通用的语法。例如,使用 $((1+2)) 进行算术运算,这是POSIX兼容的:

#!/bin/bash
echo $((1+2))

这样,无论是通过 sh hello.sh 还是 ./hello.sh 运行脚本,都会得到相同的结果。

  • sh hello.sh 使用 /bin/sh 解释器并忽略shebang行,可能不支持某些 bash 特性。
  • ./hello.sh 使用shebang行指定的解释器(如 bash),支持更多的特性。
  • 确保脚本兼容POSIX标准,可以提高其在不同环境下的可移植性。
#!/bin/bash
echo "hello,world!"

单行注释:#注释内容

多行注释:

:<<!
注释内容
!

Shell变量

Shell变量分为系统变量和用户变量,系统变量有$HOME, $PWD, $SHELL, $USER等。

Shell中定义变量:变量名=值,在Shell中不要乱加空格,在使用变量时需要在前面加上$

撤销变量:unset 变量。声明静态变量:readonly 变量,静态变量不能被撤销

将命令的返回值赋值给变量:A=`date`,用反引号表示里面是一条命令,如果不加反引号会默认是一个字符串,或者A=$(date)也可以

#!/bin/bash
#定义变量A
A=100
#输出变量
#从结果可以看出无论是否加双引号都可以
echo 1A=$A
echo "2A=$A"
#撤销变量A
#撤销后就相当于这个变量不存在了
unset A
echo "3A=$A"
#定义静态变量B,不能撤销
readonly B=2
echo "1B=$B"
#不能撤销会报错
#unset B
#echo "2B=$B"
C=`date`
D=$(date)
echo "C=$C"
echo "D=$D"
#多行注释不会输出
:<<!
echo "注释内容"
!
1A=100
2A=100
3A=
1B=2
C=Thu May 16 03:29:45 PM CST 2024
D=Thu May 16 03:29:45 PM CST 2024

配置环境变量:

export 变量名=变量值
source 配置文件
echo $变量名

#修改/etc/profile文件中的环境变量
export TOMCAT_HOME=/opt/tomcat
#生效配置文件
source /etc/profile
#输出变量
echo $TOMCAT_HOME

位置参数变量:执行shell脚本的时候会希望能获取到命令行的参数信息,如./hello.sh 100 200

$n$0代表命令本身,$1代表接收到的第一个参数,如果是10以上的参数需要加括号${10}

$*:(变成一整个字符串来存储)

  1. 在参数引用中,将所有参数作为一个单独的字符串引用。所有参数被合并成一个字符串,每个参数之间用第一个字符的值分隔(默认是空格)。
  2. 在双引号中,将所有参数合并成一个字符串,每个参数之间用IFS(内部字段分隔符)分隔。默认情况下,IFS是空格。

$@:(变成一个列表来存储)

  1. 在参数引用中,将所有参数作为独立的字符串引用。每个参数保持独立,并且在被引用时,不会合并成一个单独的字符串。
  2. 在双引号中,将每个参数保持独立。即使在双引号内,每个参数仍然是独立的字符串。

$*加双引号其实是变成了一个插值字符串

$#:接收到的命令行中所有参数的个数

#!/bin/bash

echo "Using \$*:"
for arg in $*; do
    echo "$arg"
done

echo "Using \$@:"
for arg in $@; do
    echo "$arg"
done

echo "Using \"\$*\":"
for arg in "$*"; do
    echo "$arg"
done

echo "Using \"\$@\":"
for arg in "$@"; do
    echo "$arg"
done

echo "NUM=$#"
Using $*:
10
20
30
40
Using $@:
10
20
30
40
Using "$*":
10 20 30 40
Using "$@":
10
20
30
40
NUM=4

预定义变量

$$:返回当前进程的PID

$!:返回后台运行的最后一个进程的进程号

$?:返回最后一个执行命令的返回状态。若为0表示上一条命令正确执行,否则表示上一条命令执行错误。

在后台运行脚本的方式:/root/home/shcode/myshell.sh &,在命令后加&

运算符

基本语法:$((运算式))$[运算式]expr m + n

建议使用$[]$即可

其中expr运算符之间要有空格,如果希望将expr的结果赋值给变量,需要加反引号``

在expr中乘法是\*,除法和取余是正常的/,%,在前一个运算式中乘法是正常的*

#!/bin/bash
#(2+3)*4
RES1=$(((2+3)*4))
#expr的结果赋值需要加反引号
#expr的运算符之间需要有空格
TEMP=`expr 2 + 3`
RES2=`expr $TEMP \* 4`
echo "RES1=$RES1"
echo "RES2=$RES2"

#接受命令行输入并求和
SUM=$(($1+$2))
echo "SUM=$SUM"

条件判断

判断条件(这种比较一般只用于[]的判断中):

  1. 字符串比较:=

  2. 整数比较:-lt小于,-le小于等于,-eq等于,-gt大于,-ge大于等于,-ne不等于

  3. 按照文件权限进行判断:-r读权限,-w写权限,-x执行权限

  4. 按照文件类型进行判断:-f文件存在并且是一个常规文件,-e文件存在,-d文件存在并是一个目录

基本格式:

[ condition ]前后必须要有空格,非空返回true(0为true),否则返回false。

if [ condition ]
then
	语句
elif [ condition ]
then
	语句
fi

[ condition ] && A || B:若条件成立执行A,不成立则执行B。

#!/bin/bash
#"ok"是否等于"ok"
if [ "OK"="OK" ]
then
        echo "equal"
fi
#23是否大于等于22
if [ 23 -ge 22 ]
then
        echo "greater or equal"
fi
#/root/shcode/aaa.txt 目录中的文件是否存在
if [ -f /root/shcode/aaa.txt ]
then
        echo "exist"
fi
#只要不空即为true
if [ hspedu ]
then
        echo "hspedu"
fi
#条件表达式
[ "OK"="OK" ] && echo "equal" || echo "no equal"
#多重判断
if [ $1 -ge 60 ]
then
        echo "pass"
elif [ $1 -lt 60 ]
then
        echo "fail"
fi
equal
greater or equal
hspedu
equal
fail

分支判断

基本格式:

case $变量名 in
"值1")
语句1
;;
"值2")
语句2
;;
*)
语句
;;
esac
#case
case $2 in
"1")
echo "语句1"
;;
"2")
echo "语句2"
;;
*)
echo "其他语句"
;;
esac

循环结构

for基本语法

for 变量 in 值1, 值2, ...
do
	代码
done


for ((初始值;循环控制条件;变量变化))
do
	代码
done

这里会出现一个问题:使用第二种C风格的for循环时,不能使用sh来运行,必须通过chmod加权限在./执行

#!/bin/bash
#打印命令行输入的参数
for i in "$@"
do
        echo "i=$i"
done

#计算1到第一个输入的数的和
SUM=0
for(( i=1; i<=$1; i++ ))
do
        SUM=$(($SUM + $i))
done
echo "SUM=$SUM"

while基本语法

while [ 条件判断 ]
do
	代码
done
#!/bin/bash
#接受从键盘的输入,并统计从1加到n的和
SUM=0
i=0
while [ $i -le $1 ]
do
        SUM=$(($SUM+$i))
        i=$(($i+1))
done
echo "SUM=$SUM"

读取输入

read (选项) (参数)

选项:-p:读取指令时的提示符 -t:指定读取等待时间(秒)

参数:指定读取值的变量名

#!/bin/bash
read -p "请输入一个数NUM=" NUM
echo "NUM=$NUM"

函数

分为系统函数和自定义函数。

basename [pathname]返回完整路径的最后的文件名,dirname [pathname]返回完整路径前面的路径

basename /root/shcode/aaa.txt
输出:aaa.txt
dirname /root/shcode/aaa.txt
输出:/root/shcode

自定义函数:

function myFunction {
	代码
}

定义函数后,可以直接通过函数名来调用。函数中的参数通过$1$2来访问

Shell函数不能直接返回值,但可以通过 echo 或设置全局变量来返回结果。

return 关键字在Shell中用于返回一个状态码(退出码),通常表示成功或失败。

#!/bin/bash

# 定义函数
function add {
	#内部通过$1$2来使用值
    local result=$(( $1 + $2 ))
    echo $result
}

function subtract {
    local result=$(( $1 - $2 ))
    echo $result
}

# 使用 read 命令获取用户输入
read -p "Enter first number: " n1
read -p "Enter second number: " n2

# 调用函数并获取结果
sum=$(add $n1 $n2)
diff=$(subtract $n1 $n2)

# 输出结果
echo "Sum: $sum"
echo "Difference: $diff"
posted @ 2024-05-17 14:53  钰见梵星  阅读(11)  评论(0编辑  收藏  举报