Linux Shell 学习
可以执行的 Shell 脚本
建立一个 shell 脚本文件,文件名为 test1.sh,内容如下:
echo "Hello world!"
写完之后要赋予可执行权限:
chmod +x test1.sh
然后就可以运行了。
变量
变量可以包含字母、数字和下划线,但不能以数字开头。变量名区分大小写。使用大写字母表示常量。
避免使用系统保留的变量名,如 PATH、HOME 等。同时避免使用 $ ? 等特殊字符。
Shell 赋值等号两边不能有空格。
PI=3.14159
count=0
for i in `ls /etc`; do
let count+=1
done
echo $count
使用 let 可以进行算术运算:
let count+=1
上面这个例子中,count 的值会在每次循环中加 1,以此可以输出 /etc 目录下的文件数量。
使用 $(()) 可以进行算术运算:
count=$((count + 1))
对于浮点数的计算,Shell 本身不支持浮点数运算,可以使用 bc 命令:
PI=3.14
echo "$(echo "$PI * 2" | bc)"
输出 6.28。
可以使用 readonly 来定义只读变量:
readonly PI=3.14159
可以使用 unset 来删除变量:
unset PI
特殊变量: 有一些特殊变量在 Shell 中具有特殊含义,例如
$0表示脚本的名称,$1,$2, 等表示脚本的参数。$#表示传递给脚本的参数数量,$?表示上一个命令的退出状态等。
字符串中,转义字符与其它大多数语言一样适用。但是 echo 命令中,转义字符可能需要使用 -e 参数才能生效。
# Test backslash
str="Let's go for \"me\". \nTry your best!"
echo -e $str
字符串拼接可以任意轻易组合:
str_out="You might say: \""${str}"\"\nThat's right!"
echo -e $str_out
需要注意的是,Shell 中使用 $ 使用变量必须跃出字符串引号。
使用 ${#} 可以获取字符串的长度:
str="Hello world!"
echo ${#str} # 输出 12
使用 ${:-} 可以获取字符串的子串:
str="Hello world!"
echo ${str:6:5} # 输出 world
${str:6:5} 表示从索引 6 开始,取 5 个字符。
使用 expr 可以查找字符串中某个字符的位置:
str="Hello world!"
echo `expr index "$str" o` # 输出 5
注意: expr 只能查找单个字符的位置,不能查找字符串。如果写作 expr index "$str" "world",会查找 world 中任意字符首个出现的位置,即 3(l 出现于第三个字符)。
数组
Shell 中的数组可以使用括号 () 来定义,元素之间用空格分隔:
arr1=(1 1 4 5 1 4)
或者
arr2=(
1
1
4
5
1
4
)
使用下标法访问数组元素:
arr1=(1 1 4 5 1 4)
echo ${arr1[2]}
输出 4。
使用 @ 或 * 可以获取数组的所有元素:
echo ${arr1[@]}
echo ${arr1[*]}
二者输出是等效的。
获取数组长度的方法与获取字符串长度的方法类似:
echo ${#arr1[*]}
使用 declare -A 可以定义关联数组:
declare -A arr=(["inner"]=0 ["center"]=1 ["outer"]=0)
echo ${arr["center"]}
使用 ! 返回所有键:
echo ${!arr[@]}
输出 6。
注释
单行 #,多行 :
例如:
# 这是单行注释
:<<EOF
注释注释注释
注释注释
EOF
echo 1
:<<'COMMENT'
注释
注释注释
COMMENT
:<<'
注释1
注释2
'
ls -l
:<<!
注释注释
Acceed
注释
!
:'
多行内容
字符串注释
'
传递参数
Shell 预留了一些特殊变量来获取传递给脚本的参数:
| 变量 | 含义 |
|---|---|
$0 |
脚本的名称 |
$1 |
第一个参数 |
$2 |
第二个参数 |
| … | … |
$# |
参数的个数 |
$* |
所有参数,作为一个字符串 |
$@ |
所有参数,作为一个数组 |
$? |
上一个命令的退出状态 |
$$ |
当前 Shell 的进程 ID |
$! |
上一个后台命令的进程 ID |
运算符
原生 Bash 不支持基本数学运算,只能使用 awk、bc 或 expr 等工具来进行数学运算。
常用 expr 进行表达式计算,例如:
#! /bin/bash
val=`expr 1 + 2 \* 3`
echo $val
算术运算要使用 [] 确定条件,并且在 [] 内部的运算符两边必须有空格。
if [ $val < 5 ]
then
echo "val < 5"
fi
if [ $val > 5 ]
then
echo "val > 5"
fi
关系运算符:
| 运算符 | 含义 |
|---|---|
-eq |
等于 |
-ne |
不等于 |
-gt |
大于 |
-lt |
小于 |
-ge |
大于等于 |
-le |
小于等于 |
关系运算符仅支持数字。
val=10
if [ $val -eq 10 ]
then
echo "val = 10"
fi
布尔运算符:
| 运算符 | 含义 |
|---|---|
! |
非 |
-o |
或 |
-a |
与 |
val1=10
val2=20
if [ $val1 -eq 10 -a $val2 -eq 20 ]
then
echo "val1 = 10 and val2 = 20"
else
echo "val1 != 10 or val2 != 20"
fi
逻辑运算符:
| 运算符 | 含义 |
|---|---|
&& |
与 |
| | |
或 |
val1=10
val2=20
if [ $val1 -eq 10 ] && [ $val2 -eq 20 ]
then
echo "val1 = 10 and val2 = 20"
else
echo "val1 != 10 or val2 != 20"
fi
字符串运算符:
| 运算符 | 含义 |
|---|---|
= |
等于 |
!= |
不等于 |
-z |
字符串长度为零 |
-n |
字符串长度不为零 |
$ |
字符串不为空 |
str1="Hello world!"
str2="Hello world!"
if [ $str1 = $str2 ]
then
echo "str1 = str2"
fi
此处运算没有使用 ==,而是使用了 =,因为 = 是字符串比较运算符,而 == 是模式匹配运算符。
文件测试运算符:
| 运算符 | 含义 | 记忆标记 |
|---|---|---|
-b file |
文件存在且是块设备文件 | block |
-c file |
文件存在且是字符设备文件 | character |
-d file |
文件存在且是目录 | directory |
-e file |
文件存在 | exists |
-f file |
文件存在且是普通文件 | file |
-g file |
文件存在且设置了 SGID 位 | GID |
-k file |
文件存在且设置了粘滞位 | sticKy |
-p file |
文件存在且是命名管道 | pipe |
-r file |
文件存在且可读 | readable |
-s file |
文件存在且不为空 | size |
-t fd |
文件描述符存在且指向终端 | terminal |
-u file |
文件存在且设置了 SUID 位 | UID |
-w file |
文件存在且可写 | writable |
-x file |
文件存在且可执行 | eXecutable |
-S file |
文件存在且是套接字 | Socket |
-L file |
文件存在且是符号链接 | Link |
#! /bin/bash
file="./test1.sh"
if [ -r $file ]
then
echo "文件可读"
else
echo "文件不可读"
fi
if [ -w $file ]
then
echo "文件可写"
else
echo "文件不可写"
fi
if [ -x $file ]
then
echo "文件可执行"
else
echo "文件不可执行"
fi
if [ -f $file ]
then
echo "文件为普通文件"
else
echo "文件为特殊文件"
fi
if [ -d $file ]
then
echo "文件是个目录"
else
echo "文件不是个目录"
fi
if [ -s $file ]
then
echo "文件不为空"
else
echo "文件为空"
fi
if [ -e $file ]
then
echo "文件存在"
else
echo "文件不存在"
fi
echo
| 选项 | 含义 |
|---|---|
-n |
不换行输出 |
-e |
启用转义字符 |
输出内容到文件:
#! /bin/bash
echo "Hello!" > ./story.txt
echo "This is a story of ..." >> story.txt
其中,使用 > 会覆盖文件内容,而使用 >> 会追加内容到文件末尾。
使用 ANSI 转义码可以实现彩色文本,但是用途不大。
printf
这是个 C 语言风格的输出函数,使用起来比 echo 更加灵活。
printf "Time now: %s\n" "$(date)"
输出:
Time now: 2026年 8月 26號 星期三 12:06:03 CST
test
用于文件测试。
基本语法如下:
test expression
| 选项 | 含义 |
|---|---|
-e |
文件存在 |
-f |
文件存在且是普通文件 |
-d |
文件存在且是目录 |
-r |
文件存在且可读 |
-w |
文件存在且可写 |
-x |
文件存在且可执行 |
-s |
文件存在且不为空 |
-L |
文件存在且是符号链接 |
例如,检测 /etc/passwd 文件基本情况:
#! /bin/bash
file="/etc/passwd"
if [ -e "$file" ]; then
echo "$file is exist."
if [ -r "$file" ]; then
echo "$file can be read."
fi
else
echo "$file is not exist."
fi
字符串比较:
| 选项 | 含义 |
|---|---|
-z string |
字符串长度为零 |
-n string |
字符串长度不为零 |
string1 = string2 |
字符串相等 |
string1 != string2 |
字符串不相等 |
示例
- 检查 nginx 服务的运行状态:
#! /bin/bash
service="nginx"
if systemctl is-active --quiet "$service"; then
echo "$service is running."
else
echo "$service is not running."
fi
流程控制
Shell 的流程控制有常见的几种。
if else 语句
语法:
if [ condition ]; then
# commands
elif [ condition ]; then
# commands
else
# commands
fi
同时,判断条件可以使用 (()) 来进行算术比较运算,使用 [[ ]] 来进行字符串比较,也可使用 test 命令来进行比较。
for 循环
语法:
for var in list
do
# commands
done
while 循环
语法:
while [ condition ]
do
# commands
done
until 循环
语法:
until [ condition ]
do
# commands
done
case 语句
语法:
case "$variable" in
pattern1)
# commands
;;
pattern2)
# commands
;;
*)
# commands
;;
esac
例如,判断输入数字作为选项:
#! /bin/bash
read num
case $num in
1)
echo "You chose 1."
;;
2)
echo "You chose 2."
;;
3)
echo "You chose 3."
;;
*)
echo "Illegal input."
;;
esac
break 和 continue
使用方法与 C 语言基本一致。
函数
函数的定义格式如下:
[function] function_name [()]
{
# commands
[return int;]
}
例如:
#! /bin/bash
demo() {
echo "This is an empty funcion."
}
demo
注意: return 语句只能返回整数,而且只能返回 0 ~ 225 之间的整数。
函数获取参数的方法与脚本获取参数的方法相同,使用 $1, $2, 等表示函数的参数,而且不能在定义的时候给函数命名参数。
demo() {
echo "This is a function with parameters."
echo "$1 is the first parameter."
}
输入 / 输出重定向
| 命令 | 含义 |
|---|---|
command > file |
将标准输出重定向到文件,覆盖原有内容 |
command < file |
将标准输入重定向到文件 |
command >> file |
将标准输出重定向到文件,追加内容 |
n > file |
将文件描述符 n 重定向到文件 |
n >> file |
将文件描述符 n 重定向到文件,追加内容 |
n >& m |
将文件描述符 n 重定向到文件描述符 m (合并) |
n <& m |
将文件描述符 n 重定向到文件描述符 m (合并) |
<< tag |
将标准输入重定向到当前脚本中,直到遇到指定的 tag |
例如,使命令不要任何的输出:
command > /dev/null 2>&1
注意:此处 2>&1 表示将标准错误输出重定向到标准输出,/dev/null 是一个特殊的设备文件,表示丢弃所有写入它的数据。2 与 > 间不能有空格,否则 2 会被当作一个普通的文件名。
文件包含
使用 . 或 source 包含一个 Shell。
例如 test1.sh 文件内容如下:
counter=0
test2.sh 文件内容如下:
#! /bin/bash
. ./test1.sh
echo $counter
此处,test2.sh 文件中使用 . 来包含 test1.sh 文件,从而可以访问 test1.sh 中定义的变量 counter。
本文完…
参考教程:Runoob Shell 教程
本文根据此教程内容整理删减,适合具有一定基础的读者阅读,以达到快速掌握 Shell 的目的。
