Shell该怎么写

Soul Lv2

时隔这么长时间,我终于又有时间蓝更新内容了,这期是对 shell 基本语法的一个总结,话不多说,我们直接开始。

第一个 shell 脚本

Shell 的种类其实有很多种,例如几乎所有的 linux 发行版的默认 shell 都是 bin/bash,也就是我常说的 bash, 以及我们实际中比较常用的 zsh 等,但处于通用性考虑,大多数的时候我们都会选择 bash, 来保证在大部分设备上可以运行。在 shell 中 helloworld 一般可以这么写:

1
2
#!/bin/bash
echo "Hello World!"

第一行是对所使用的解释器的声明,是一个固定的格式。
将上面的代码文件命名为 test.sh, 然后在终端中执行下面的命令

1
2
chmod +x ./test.sh
./test.sh

就可以看到控制台输出了我们想要的内容

变量

接下来是基本的变量声明,变量声明的语法如下:

1
2
name='张三'
age=18

值得一提的是在 shell 中要求等号两侧不能存在空格,也就是说像下面这样的语句:

1
age = 18

是无法顺利执行的。

如果需要的是数组,可以使用类似下面的语句声明:

1
2
3
array=(A B C 'D' E)
nums=([2]=2 [0]=0 [1]=1)
colors=(red yellow "dark blue")

在声明了变量之后就可以使用了,一般的格式为 $var 或者 ${var},大括号可以方便解释器识别变量的范围,所以一般推荐使用大括号。可以参考下面的代码

1
2
3
4
5
6
7
echo ${age}
echo 'my age is ${age}'
echo "my age is ${age}"
#输出为
# 18
# my age is #{age}
# my age is 18

注意到区别了吗,在双引号包围的字符串中 shell 可以识别变量并自动将变量名替换为对应的值

如果我们需要访问的是一个数组,那么可以使用类似于

1
2
3
4
5
6
7
8
array=(1 2 3 4)
echo ${array[0]}
echo ${array[*]}
echo ${array[@]}
#分别输出
#1
#1 2 3 4
#1 2 3 4

可以注意到 *@ 都可以表示数组的全部值,但二者之间也存在微妙的区别。前者是直接将数组的全部内容作为一个字符串去输入,而后者实际上是将数组按照元素输入,看下面的代码:

1
2
3
array=(2 3 4 5)
array=(0 1 "#{array[@]}" 6 7 ) # 数组包含的元素为0 1 2 3 4 5 6 7,共8个
array=(0 1 "#{array[*]}" 6 7 ) # 数组包含的元素为0 1 “2 3 4 5” 6 7,共5个

我们也可以直接利用@来访问特定的数组内容

1
2
3
nums=(0 1 2 3)
echo ${nums[@]:0:2}
# 输出0 1

上面的例子中 ${array[@]} 扩展为整个数组,:0:2 取出了数组中从 0 开始,长度为 2 的元素。

关于变量,可能用到的操作还包括下面这些:

1
2
3
4
num=1
readonly num #设置为只读变量
unset num #删除变量,只读变量不可删除
unset array[1] # 删除数组中特定的元素

此外,还存在一些可以在任何地方访问的环境变量

变量描述
$HOME当前用户的用户目录
$PATH用分号分隔的目录列表,shell 会到这些目录中查找命令
$PWD当前工作目录
$RANDOM0 到 32767 之间的整数
$UID数值类型,当前用户的用户 ID
$PS1主要系统输入提示符
$PS2次要系统输入提示符

运算符

接下来我们讲讲 shell 中十分抽象的一部分内容:运算符逻辑,先看几个类人的:

运算符说明举例
+加法expr $x + $y 结果为 30。
-减法expr $x - $y 结果为 -10。
*乘法expr $x \* $y 结果为 200。
/除法expr $y / $x 结果为 2。
%取余expr $y % $x 结果为 0。
=赋值x=$y 将把变量 y 的值赋给 x。
==相等。用于比较两个数字,相同则返回 true。[ $x == $y ] 返回 false。
!=不相等。用于比较两个数字,不相同则返回 true。[ $x != $y ] 返回 true。
首先必须要提到的是原生的 shell 是不支持数学计算的,所以我们如果要完成一个简单的加法需要这样写:
1
2
3
x=10
y=20
z=`expr ${x} + ${y}`

注意使用的是反引号而不是单引号, 且乘号之前必须加反斜杠转义

此外,条件表达式必须放到方括号内,且必须有空格,例如 [$x==$y] 会报错而 [ $x == $y] 是正常的。

然后是关系运算符:关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例
-eq检测两个数是否相等,相等返回 true。[ $a -eq $b ] 返回 false。
-ne检测两个数是否不相等,不相等返回 true。[ $a -ne $b ] 返回 true。
-gt检测左边的数是否大于右边的,如果是,则返回 true。[ $a -gt $b ] 返回 false。
-lt检测左边的数是否小于右边的,如果是,则返回 true。[ $a -lt $b ] 返回 true。
-ge检测左边的数是否大于等于右边的,如果是,则返回 true。[ $a -ge $b ] 返回 false。
-le检测左边的数是否小于等于右边的,如果是,则返回 true。[ $a -le $b ] 返回 true。

进一步的如果需要使用布尔运算,参考下面的符号:

运算符说明举例
!非运算,表达式为 true 则返回 false,否则返回 true。[ ! false ] 返回 true。
-o或运算,有一个表达式为 true 则返回 true。[ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a与运算,两个表达式都为 true 才返回 true。[ $a -lt 20 -a $b -gt 100 ] 返回 false。

然后是两个逻辑运算的符号:

运算符说明举例
&&逻辑的 AND[[ ${x} -lt 100 && ${y} -gt 100 ]] 返回 false
||逻辑或 OR[[ ${x} -lt 100 || ${y} -gt 100 ]] 返回 true

还有比较特别的专门用于字符串的几个:

运算符说明举例
=检测两个字符串是否相等,相等返回 true。[ $a = $b ] 返回 false。
!=检测两个字符串是否相等,不相等返回 true。[ $a != $b ] 返回 true。
-z检测字符串长度是否为 0,为 0 返回 true。[ -z $a ] 返回 false。
-n检测字符串长度是否为 0,不为 0 返回 true。[ -n $a ] 返回 true。
str检测字符串是否为空,不为空返回 true。[ $a ] 返回 true。
还有一些专门用于文件的
操作符说明举例
-b file检测文件是否是块设备文件,如果是,则返回 true。[ -b $file ] 返回 false。
-c file检测文件是否是字符设备文件,如果是,则返回 true。[ -c $file ] 返回 false。
-d file检测文件是否是目录,如果是,则返回 true。[ -d $file ] 返回 false。
-f file检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。[ -f $file ] 返回 true。
-g file检测文件是否设置了 SGID 位,如果是,则返回 true。[ -g $file ] 返回 false。
-k file检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。[ -k $file ] 返回 false。
-p file检测文件是否是有名管道,如果是,则返回 true。[ -p $file ] 返回 false。
-u file检测文件是否设置了 SUID 位,如果是,则返回 true。[ -u $file ] 返回 false。
-r file检测文件是否可读,如果是,则返回 true。[ -r $file ] 返回 true。
-w file检测文件是否可写,如果是,则返回 true。[ -w $file ] 返回 true。
-x file检测文件是否可执行,如果是,则返回 true。[ -x $file ] 返回 true。
-s file检测文件是否为空(文件大小是否大于0),不为空返回 true。[ -s $file ] 返回 true。
-e file检测文件(包括目录)是否存在,如果是,则返回 true。[ -e $file ] 返回 true。

控制语句

接下来是简单的控制语句,不过在开始之前我们首先要了解一下我们的上面那些奇奇怪怪的运算符都是哪里来的。事实上那些奇奇怪怪的运算符都是 test 命令的参数,方括号内的内容实际上被传递给了 test 命令,由这个命令来判断布尔值,更进一步的我们还有两个比较特殊的符号:

  • [[]] 支持了正则表达式,并提供了更安全的字符串处理例如:
1
2
3
if [[ "${var}" == *.log ]]; then
echo "is log"
fi
  • (()) 支持了更灵活的数学运算,包括大于小于等操作,例如
1
2
3
if (( ${var} > 12 )); then
echo "true"
fi

接下来了解最常见的 if-else 语句。在 shell 中一般的格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
a=10  
b=20
if [ $a == $b ]
then
   echo "a 等于 b"
elif [ $a -gt $b ]
then
   echo "a 大于 b"
elif [ $a -lt $b ]
then
   echo "a 小于 b"
else
   echo "没有符合的条件"
fi

值得一提的是,与其他语言不同,shell 中不允许存在空的 elif 块,如果不需要进行处理,就直接省略不写

然后是循环语句,循环语句中比较常用的有 for 循环和 while 循环,先看 while 循环,和 if-else 类似,格式如下:

1
2
3
4
5
6
int=0
while ((int<10))
do
echo $int
let "int++"
done

上面我们使用了 let 命令来操作变量,let 命令可以方便的实现自增自减等操作。
然后是 for 循环,一般的格式如下:

1
2
3
4
for loop in 1 2 3 4 #如果是1到100可以简写为{1..100}
do
echo $loop
done

loop 会分别被赋值为 1 2 3 4。实际上 in 后面可以加任意一个数组,就像下面这样

1
2
3
4
5
6
7
8
9
for str in This is a string
do
echo $str
done
#打印结果为:
#This
#is
#a
#string

更进一步的例如下面这一部分的代码:

1
2
3
4
for file in /hone/shell/*
do
mv "${file}" "/hone/scripts"
done

就可以直接将 /hone/shell 下的所有文件移动到 /home/scripts
上述的两种循环均支持 break 和 continue 语句;

然后还有一个长得比较抽象的 sitch-case 语句,类似下面这样:

1
2
3
4
5
6
7
8
9
10
site="baidu"  

case "$site" in
   "baidu") echo "百度"
   ;;
   "google") echo "Google 搜索"
   ;;
   "taobao") echo "淘宝网"
   ;;
esac

函数

在 shell 中函数的定义格式如下:

1
2
3
function(){
dosomething
}

对于任意的函数,都可以提供任意个参数,在函数内部,我们可以使用特定的名称来引用参数

1
2
3
4
5
6
7
8
9
10
funWithParam(){  
    echo "第一个参数为 $1 !"
    echo "第二个参数为 $2 !"
    echo "第十个参数为 $10 !"
    echo "第十个参数为 ${10} !"
    echo "第十一个参数为 ${11} !"
    echo "参数总数有 $# 个!"
    echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73

具体的可用参数见下表:

变量描述
$0脚本名称
$1 … $9第 1 个到第 9 个参数列表
${10} … ${N}第 10 个到 N 个参数列表
$* or $@除了$0外的所有位置参数
$#不包括 $0 在内的位置参数的个数
$FUNCNAME函数名称(仅在函数内部有值)
如果函数需要有返回值,有两种选择,
1
2
3
4
5
6
7
8
9
10
fun(){
dosomething
return int #int的范围是0到255
}
fun2(){
dosomething
echo result #在没有return的情况下最后一行的执行结果将被视为返回值
}
fun2
echo $? #此时可以使用$?来获取最后的返回值

结语

到这里该讲的就差不多都讲了,shell 的另一个大板块就是各种各样的命令的作用,具体可以参考 Linux常用命令汇总

  • 标题: Shell该怎么写
  • 作者: Soul
  • 创建于 : 2025-08-21 09:55:04
  • 更新于 : 2025-08-26 16:00:15
  • 链接: https://soulmate.org.cn/posts/8b777feb/
  • 版权声明: 本文章采用 CC BY-NC 4.0 进行许可。