Shell编程
Shell 简介
- Linux Shell 是 Linux 操作系统中用于与内核进行交互的命令行界面。它是用户与操作系统之间的中介,允许用户通过输入指令(命令)来执行操作,如文件管理、程序执行、设备控制等。
- Shell 本身是一个程序,当用户登录到 Linux 系统时,它提供了一个用户会话,并解释用户输入的命令。Linux Shell 同时也是一种脚本语言,可以编写脚本(一系列的命令)来自动化常见的任务,提高工作效率。
Shell 类型
- Linux 操作系统支持多种 Shell,每种都有其独特的功能和特性。以下是一些最常见的 Shell 类型:
- Bash (Bourne Again Shell): 是 Bourne Shell 的后继版本,由 GNU 项目创建,成为大多数 Linux 发行版的默认 Shell。
- Sh (Bourne Shell): 是 Unix Shell 的原始版本,由 Steve Bourne 编写,现在已经被它的各种扩展版本如 Bash 所取代。
- Dash (Debian Almquist Shell): 是 Almquist Shell (ash) 的 Debian 版本,以速度快而闻名,用于满足 POSIX 标准要求。
- Ksh (Korn Shell): 由 David Korn 开发,结合了 C Shell 和 Bourne Shell 的特点,提供了许多高级的编程功能。
- Csh (C Shell): 由 Bill Joy 创建,其语法类似于 C 语言,主要在某些特定系统用户中流行。
- Tcsh (TENEX C Shell): 是 C Shell 的增强版本,提供了命令行编辑和历史功能。
- Zsh (Z Shell): 结合了 Bash、ksh 和 tcsh 的特点,提供了许多强大的特性,比如更好的自动补全和命令修正功能。
- Fish (Friendly Interactive Shell): 是一个相对较新的、用户友好的 Shell,以其易用性和高级的特性为特点,例如自动建议和丰富的颜色化输出。
- 每种 Shell 都有其特定的用途和功能,用户可以根据自己的喜好和需求选择合适的 Shell 环境。例如,Bash 由于其广泛的支持和灵活性,通常是用户的首选。而 Zsh 因其丰富的特性和定制能力,特别受到高级用户和开发者的喜爱。
- 查看当前使用的 Shell
1
echo $SHELL
- 列出系统上可以用的 Shell或者你可以直接查看
1
chsh -l
/etc/shells
文件来了解系统上有哪些可用的 shell:1
cat /etc/shells
如何执行 Shell 脚本 ?
- 给予执行权限:
首先,确保你的脚本文件具有执行权限。你可以使用chmod
命令来添加执行权限。例如,如果你的脚本文件名为script.sh
,你可以使用以下命令:1
chmod +x script.sh
- 直接运行脚本:
如果脚本文件已经有了执行权限,你可以通过在脚本所在目录下使用下面的命令来直接运行它:1
./script.sh
这里的
./
表示当前目录。 - 使用 Shell 运行脚本:
你还可以通过调用特定的 Shell 来执行脚本,即使脚本没有执行权限。例如,如果你想用 Bash 运行你的脚本,你可以使用以下命令:或者如果是用 sh(Bourne Shell):1
bash script.sh
1
sh script.sh
- 使用路径运行脚本:
如果脚本位于你的PATH
环境变量所列的目录之一,你可以从任何地方运行它而不需要指定路径。
- 注意事项
- 确保脚本的第一行包含了shebang(#!),它告诉系统使用哪个解释器来执行文件。例如,如果你的脚本是用Bash编写的,那么脚本的第一行应该是:
1
- 如果是用 Python 写的脚本,第一行可能会是:
1
- 确保在尝试执行脚本之前,脚本是针对你的系统和环境正确编写的。如果脚本中有错误或者编写的环境与执行的环境不兼容,它可能无法正确运行。
- 确保脚本的第一行包含了shebang(#!),它告诉系统使用哪个解释器来执行文件。例如,如果你的脚本是用Bash编写的,那么脚本的第一行应该是:
Shell 变量
-
简介
在 Linux Shell 中,变量是存储值的标识符。在 Shell 脚本和命令行中,变量用于保存数据,如文本字符串、数字、文件名等,以便在需要的时候重复使用。
-
设置变量
要在 Shell 中设置变量,您可以使用等号(
=
)将值分配给变量名。在等号的两侧不应有空格。例如:1
my_variable="Hello, World!"
-
访问变量
访问变量的值时,您需要在变量名前加上美元符号(
$
)。例如:1
echo $my_variable
也可以使用
${variable}
这种格式来引用变量,这与直接使用$variable
效果是相同的。这种格式在变量周围添加了大括号,这样可以帮助解释器区分变量名和紧随其后的字符。通常在变量和文字紧密结合时这种方法非常有用。1
2my_variable="World"
echo "Hello, ${my_variable}!" -
环境变量
-
查看环境变量
使用
env
或printenv
命令来列出所有的环境变量及其值。1
env
或
1
printenv
-
打印指定环境变量
使用
echo
命令结合美元符号($
)和大括号来打印指定的环境变量。例如,打印PATH
环境变量:1
echo $PATH
或
1
echo ${PATH}
PATH
是在大多数操作系统中使用的一个环境变量,尤其是在 Unix 和类 Unix 系统中,如 Linux、MacOS、BSD 以及 Windows 的命令行环境。它定义了一个由冒号(在 Windows 中是分号)分隔的目录列表,这些目录被用来查找和执行命令行中输入的可执行文件。 -
修改环境变量
要修改一个环境变量,你可以导出新的值。例如,添加一个新路径到
PATH
环境变量,可以这样做:1
export PATH=$PATH:/new/path
这样不会移除
PATH
当前的内容,而是在尾端追加了:/new/path
。 -
设置新的环境变量
要创建一个新的环境变量,可以直接使用
export
命令:1
export MY_VARIABLE="my_value"
-
持久化环境变量
环境变量的更改默认只在当前 shell 会话中有效。要想持久化环境变量的更改,需要将
export
命令添加到你的 shell 配置文件中,例如~/.bashrc
或~/.profile
(对于 Bash 用户),或者是全局文件如/etc/environment
。1
echo 'export MY_VARIABLE="my_value"' >> ~/.bashrc
然后,使用
source
命令来应用改变或重新登录。1
source ~/.bashrc
-
删除环境变量
要删除环境变量,可以使用
unset
命令:1
unset MY_VARIABLE
-
-
位置参数
在编写脚本时,位置参数变量
$1
、$2
、$3
等用于访问脚本的命令行参数。例如,$1
是传递给脚本的第一个参数。 -
特殊变量
$0
- 脚本名$#
- 传递给脚本的参数数量$*
- 传递给脚本的所有参数的单个字符串$@
- 传递给脚本的所有参数,每个参数都是独立的引用字符串$$
- 当前 Shell 进程的进程ID(PID)$?
- 上一个命令的退出状态
-
变量扩展
Shell 提供了多种变量扩展的方法,可以用来修改变量的值、测试其状态或进行字符串操作。例如:
1
echo ${#my_variable} # 输出变量值的长度
-
只读变量
要使变量成为只读,可以使用
readonly
命令。这样可以创建一个常量,一旦设定,就无法修改其值。例如:1
readonly my_variable
尝试修改一个只读变量将会导致错误:
1
2my_variable="Try to change"
# bash: constant: readonly variable请注意,一旦你将变量设为只读,你就不能再对它进行
unset
。 -
取消变量
要取消设置变量,可以使用
unset
命令。例如:1
unset my_variable
现在,
my_variable
不再存在。
单、双、反引号的区别
-
单引号
'
- 任何在单引号内的字符都会被视为普通字符,即 shell 不会对其进行任何特殊处理。变量和命令不会被展开或执行。
- 例如:这个命令将输出
1
echo '$HOME'
$HOME
字符串本身,而不是 HOME 环境变量的值。
-
双引号
"
- 双引号内的字符大多会被当作普通字符对待,但是某些特殊字符,如美元符号 (
$
)、反引号、反斜杠 (\
),会被 shell 特殊处理,允许变量展开、命令替换或者转义字符的使用。 - 例如:这个命令将输出 HOME 环境变量的值。
1
echo "$HOME"
- 另外一个例子:这将输出当前日期和时间,因为
1
echo "The date is `date`"
date
命令会被执行并替换到字符串中。 - 特别注意
- 双引号可以防止通配符扩展,比如
*
和?
。如果你不想让 shell 扩展这些特殊字符,可以将它们放在双引号中。 - 在双引号内,如果你还想表示字面量的特殊字符(比如
$
),可以使用反斜杠 (\
) 来转义。
举个例子:这将输出类似1
echo "Value of \$HOME is $HOME"
Value of $HOME is /home/username
。
- 双引号可以防止通配符扩展,比如
- 双引号内的字符大多会被当作普通字符对待,但是某些特殊字符,如美元符号 (
-
反引号 `
- 反引号,有时也被称作倒引号、重音符或者反引号,用于在 shell 中执行命令替换。当你将一段文本或命令包围在反引号中时,shell 会先执行那个命令,然后将输出的结果替换到原来命令的位置。
- 例如:这个命令会执行
1
echo `date`
date
命令,并将它的输出作为echo
命令的参数。结果就是打印出当前的日期和时间。 - 现代 shell 编程实践中,反引号已经逐渐被
$()
所取代,因为后者可读性更好,且易于嵌套。$()
与反引号有着同样的效果,但是它使用圆括号而不是反引号。 - 使用
$()
的同一命令会是这样的:1
echo $(date)
- 这样也会执行
date
命令,但是$()
语法更加清晰,并且当需要嵌套命令替换时能更容易地阅读和理解。例如,如果你需要在命令替换中再进行命令替换,使用$()
就比使用反引号简洁得多:这会打印出当前工作目录的基本名(即最后一个目录的名字),而不是完整的路径。1
echo $(basename $(pwd))
- 总的来说,反引号和
$()
都用于命令替换,但是推荐使用$()
因为它更加现代和灵活。
-
反斜线
\
- 反斜线在 Unix 和类 Unix 操作系统的 shell 编程中扮演着很重要的角色。它主要用于以下几个方面:
- 转义字符:
反斜线用于移除紧随其后的字符的特殊意义。这在处理包含特殊字符或保留字(比如空格、*
、?
、$
、"
、'
等)的字符串时特别有用。例如,如果你想打印"$HOME"
字面量,你需要使用转义字符来防止$
被当作变量的引用来处理:1
echo "\$HOME"
- 续行符:
在 shell 脚本或命令行中,反斜线也用作续行符,用于将长命令分割成几行以提高可读性。当在行尾使用反斜线时,shell 会忽略换行符,并将下一行作为当前行的继续:1
2echo "This is a very long line, \
split over two lines." - 字符字面量:
在某些命令中,比如sed
或awk
,反斜线用于指定某些字符的字面量,如\n
代表换行符,\t
代表制表符等。 - 引用下一个字符:
在正则表达式中,反斜线用于引用下一个字符,标示它是一个特殊字符或一个普通字符。比如\.
用于匹配点(.
)字符,而不是任意字符。
- 转义字符:
- 注意:
使用反斜线时需要注意,因为它对于 shell 是特殊的,有时你可能需要使用双反斜线(\\
)来表示反斜线本身,特别是在传递给需要接受反斜线的程序时。
- 反斜线在 Unix 和类 Unix 操作系统的 shell 编程中扮演着很重要的角色。它主要用于以下几个方面:
字符串操作
-
字符串拼接
可以直接将字符串放在一起,shell 会自动拼接它们。
1
2
3
4str1="Hello,"
str2=" World!"
greeting="${str1}${str2}"
echo $greeting # 输出 Hello, World! -
获取字符串长度
使用 ${#string} 来获取字符串长度。
1
2str="Hello"
echo ${#str} # 输出 5 -
字符串切片
使用
${string:position:length}
来获取子字符串。1
2str="Hello, World!"
echo ${str:7:5} # 输出 World在 Bash shell 中,
${str:7:5}
这种参数扩展的语法表示从字符串str
的第 7 个字符(从 0 开始计数)开始提取长度为 5 的子字符串。因此,如果str
的值是"Hello, World!"
,那么${str:7:5}
的结果将会是"World"
。 -
查找子字符串
- 在 shell 脚本中,
expr
是一个用于执行表达式求值的命令,它可以用来执行数学运算、比较运算,或者对字符串执行一些操作。对于字符串,expr
命令中的index
和match
函数分别用于查找子字符串的位置和匹配字符串。 - 使用
expr index
expr index "$string" "$characters"
会返回$string
中$characters
中任何一个字符首次出现的位置,位置的计数从 1 开始。如果没有字符匹配,它返回 0。1
2
3str="Hello, World!"
pos=$(expr index "$str" "W")
echo $pos # 如果W存在于str中,将输出W首次出现的位置,从1开始计数- 在这个例子中,
"W"
是在Hello, World!
的第 8 个位置,因此expr index
会输出8
。
- 使用
expr match
或expr
expr match "$string" "$pattern"
或expr "$string" : "$pattern"
会根据$pattern
来匹配$string
的开头部分。如果匹配成功,它返回匹配的字符串长度;如果失败,它返回 0。$pattern
是一个正则表达式,它应该从字符串的开始进行匹配,而不是在中间的任意位置。这与其他语言或工具中的正则匹配有所不同,那里通常正则表达式可以在字符串中的任何位置匹配。- 示例
1
2
3str="Hello, World!"
pos=$(expr match "$str" '.*World')
echo $pos # 输出匹配到的字符串长度,如果是 .*World,则是 "Hello, World" 的长度 - 在这个例子中,正则表达式
'.*World'
匹配从开始到"World"
的子字符串。由于这个子字符串是"Hello, World"
,它的长度是 13,所以expr match
会输出13
。
- 注意事项
expr
对正则表达式的支持比较基础,并不支持所有可能习惯的正则表达式特性。expr
命令在使用时,某些字符可能需要转义,如*
、(
、)
等,因为它们有特殊的 shell 意义。- 使用
expr
执行数学运算时,表达式中的元素间需要有空格,如expr 2 + 2
而不是expr 2+2
。
expr
命令在现代 shell 脚本中使用越来越少,因为 Bash 自带的[[ ]]
测试和$(( ))
算术扩展提供了更为直观和强大的功能。
- 在 shell 脚本中,
-
替换字符串
- 使用
${string/substring/replacement}
来替换第一次出现的子字符串。 - 使用
${string//substring/replacement}
来全局替换子字符串。 - 示例
1
2str="Hello, World!"
echo ${str/World/Shell} # 输出 Hello, Shell!
- 使用
-
删除子字符串
- 使用
${string#substring}
来删除字符串开头的部分。 - 使用
${string##substring}
来贪婪地删除字符串开头的部分。 - 使用
${string%substring}
来删除字符串结尾的部分。 - 使用
${string%%substring}
来贪婪地删除字符串结尾的部分。 - 示例
1
2file="image.png"
echo ${file%.*} # 输出 image
- 使用
-
转化大小写(需要 Bash 4.0 及以上版本)
- 使用
${string,,}
来将字符串转为小写。 - 使用
${string^^}
来将字符串转为大写。 - 示例
1
2
3str="Hello World!"
echo ${str,,} # 输出 hello world!
echo ${str^^} # 输出 HELLO WORLD!
- 使用
-
比较字符串
使用
==
和!=
在[[ ]]
测试构造中比较字符串。1
2
3
4
5
6
7str1="hello"
str2="world"
if [[ $str1 == $str2 ]]; then
echo "Strings are equal."
else
echo "Strings are not equal."
fi # 输出 Strings are not equal. -
字符串截断
cut
是 Linux 和 Unix 系统中常用的命令行工具,用于按列提取文本数据的一部分。你可以指定一个分隔符,然后根据需要选择字段(列)。- 以下是
cut
命令的一些常用选项-d
:指定字段的分隔符,默认分隔符是制表符。-f
:选择要显示的字段,字段编号从 1 开始。-c
:选择要显示的字符。
- 使用
-d
和-f
选项- 当你想要根据特定的分隔符来提取字符串时,你可以使用
-d
选项来指定分隔符,使用-f
选项来指定你想要提取的字段。 - 例如,假设你有一个字符串 “name:john,age:30” 并且你想要提取 “john” 这个值,你可以通过指定分隔符为冒号
:
和逗号,
来提取第一个字段:1
2
3str="name:john,age:30"
name=$(echo $str | cut -d':' -f2 | cut -d',' -f1)
echo $name # 输出 john - 在这个例子中:
- 第一个
cut
使用-d':'
来指定冒号为分隔符,并使用-f2
来选择第二个字段,这将输出 “john,age”。 - 第二个
cut
使用-d','
来指定逗号为分隔符,并使用-f1
来选择第一个字段,这将最终输出 “john”。
- 第一个
- 当你想要根据特定的分隔符来提取字符串时,你可以使用
- 使用
-c
选项- 如果你想要根据字符位置来提取字符串,可以使用
-c
选项来指定要提取的字符范围。 - 例如,如果你有一个字符串 “12345” 并且只想提取中间的 “234”,你可以这样做:
1
2
3str="12345"
chars=$(echo $str | cut -c2-4)
echo $chars # 输出 234 - 在这个例子中,
-c2-4
表示从第二个字符开始,截取到第四个字符。 cut
命令在文本文件和管道命令中非常有用,让你能够方便地提取你需要的数据。它通常用于处理 CSV 文件、日志文件,或任何有固定分隔符的文本数据。
- 如果你想要根据字符位置来提取字符串,可以使用
数组
-
定义数组
创建一个数组通常使用圆括号
()
,元素之间用空格分开:1
array=(element1 element2 element3)
-
访问数组元素
访问数组中的元素需要使用大括号
{}
,并用索引指定元素的位置(索引从0开始):1
2echo ${array[0]} # 输出第一个元素
echo ${array[1]} # 输出第二个元素 -
获取全部数组元素
使用
@
或*
可以获取数组中的所有元素,它们将展开为数组中的所有元素,并且元素之间由 IFS 环境变量(默认为空格)分隔:1
2echo ${array[@]} # 输出element1 element2 element3
echo ${array[*]} # 输出element1 element2 element3两者在大多数情况下是可互换的,但在双引号中使用时,
"@"
会保留元素之间的空白符(如空格、换行符),而"*"
则会将所有元素合并成一个字符串。1
2
3
4
5
6
7
8
9
10
11
12
13
array=("first element" "second element" "third element")
echo "Using [@]:"
for item in "${array[@]}"; do
echo "$item"
done
echo "Using [*]:"
for item in "${array[*]}"; do
echo "$item"
done输出
1
2
3
4
5
6Using [@]:
first element
second element
third element
Using [*]:
first element second element third element总结一下:
"${array[@]}"
在双引号中使用时,会安全地处理数组元素内部的空白符,并且保持每个元素作为独立的单位,这在传递参数给命令或是遍历数组元素时特别有用。"${array[*]}"
在双引号中使用时,会将所有元素合并成一个包含空格分隔的单一字符串,这在需要将数组元素合并为一个字符串时很有帮助
-
获取数组的长度
要获取数组的长度(即元素的数量),可以使用
#
:1
echo ${#array[@]} # 输出数组元素的个数
-
修改数组元素
可以直接通过索引来修改数组中的某个元素:
1
array[0]=newElement1 # 将第一个元素修改为 newElement1
-
添加数组元素
在数组尾部添加元素可以直接指定新的索引或者不指定索引:
1
array+=(newElement) # 在数组尾部添加一个新的元素
-
删除数组元素
使用
unset
命令可以删除数组中的元素:1
unset array[1] # 删除第二个元素
请注意,使用
unset
删除元素后,数组的索引将不会重新排列。例如,如果你有一个包含4个元素的数组,然后你删除了第二个元素,数组将包含元素0、2和3(1号索引将不存在)。 -
遍历数组
使用 for 循环可以遍历数组中的所有元素:
1
2
3
4for elem in "${array[@]}"
do
echo $elem
done注意,这里使用的是
"${array[@]}"
来确保元素内部的空白符被正确处理。 -
切割数组
你可以使用
:
和数值来切割数组:1
echo ${array[@]:1:2} # 输出从第二个元素开始的2个元素
-
关联数组(类似于其他语言中的字典)
Bash 4.0 引入了关联数组,它使用字符串作为索引,而不是数字。要声明关联数组,需要使用
-A
选项:1
2
3
4
5declare -A assoc_array
assoc_array[key1]=value1
assoc_array[key2]=value2
echo ${assoc_array[key1]} # 输出 value1
seq
-
简介
- 在 Linux shell 中,
seq
是一个常用的命令,用于生成从一个数到另一个数之间的数字序列。 seq
命令非常有用,尤其在编写需要序列数据的脚本时。你可以将seq
生成的序列用于循环控制或者任何需要数列的场景。
- 在 Linux shell 中,
-
生成一个简单的数列
1
seq 1 10
这将生成从 1 到 10 的数字序列。
1
2
3
4
5
6
7
8
9
101
2
3
4
5
6
7
8
9
10 -
指定一个增量(步长)
1
seq 1 2 10
这将生成一个从 1 开始到 10 结束的序列,步长为 2,即:1, 3, 5, 7, 9。
1
2
3
4
51
3
5
7
9 -
生成倒序数列
1
seq 10 -1 1
这将生成从 10 到 1 的倒序数列。
1
2
3
4
5
6
7
8
9
1010
9
8
7
6
5
4
3
2
1 -
使用固定的宽度格式化数字
1
seq -w 0 5 50
这将生成一个从 0 到 50 的数字序列,步长为 5,并且每个数字都会被格式化为相同的宽度,通过在前面添加零来实现,例如:00, 05, 10, …, 50。
1
2
3
4
5
6
7
8
9
10
1100
05
10
15
20
25
30
35
40
45
50 -
将序列输出到一个文件
1
seq 1 100 > numbers.txt
这将生成一个从 1 到 100 的数字序列,并将其保存到
numbers.txt
文件中。 -
生成浮点数序列
1
seq 0.5 0.25 2
这将生成一个以 0.25 为步长的浮点数序列,从 0.5 开始到 2 结束。
1
2
3
4
5
6
70.50
0.75
1.00
1.25
1.50
1.75
2.00
expr
-
简介
- 在 Linux shell 中,
expr
是一个用于执行整数或字符串表达式求值的命令工具。它可以用来进行基本的数学运算、比较运算、字符串操作等。 - 请注意,在使用
expr
时,表达式中的元素需要用空格隔开。同时,因为某些字符(比如*
和(
)在 shell 中有特殊含义,可能需要使用反斜杠进行转义,以避免 shell 直接解释这些字符。
- 在 Linux shell 中,
-
基本数学运算
例如,执行加法运算:
1
expr 5 + 3
输出将会是
8
。 -
整数乘法
注意,由于星号(*)是 shell 的特殊字符,所以在用作乘法符号时,需要用反斜杠转义:
1
expr 5 \* 3
输出将会是
15
。 -
整数除法
1
expr 10 / 2
输出将会是
5
。 -
整数取模(求余数)
1
expr 10 % 3
输出将会是
1
。 -
比较运算
expr
也可以用于比较两个数值或字符串,并返回比较结果(通常是1
表示真,0
表示假):1
expr 5 \< 10
输出将会是
1
,表示 5 小于 10 的比较结果为真。 -
字符串长度
1
expr length "Hello World"
输出将会是字符串 “Hello World” 的长度,即
11
。 -
字符串索引
找出字符串 “Hello” 中字母 “l” 首次出现的位置(位置索引从 1 开始):
1
expr index "Hello" l
输出将会是
3
。 -
字符串截取
从字符串 “Hello World” 中截取第一个单词:
1
expr substr "Hello World" 1 5
输出将会是
Hello
。1
表示子字符串的起始位置(注意,在expr
中位置是从 1 开始计数的,而不是从 0 开始)。5
表示要提取的子字符串的长度。
-
正则表达式匹配
expr
命令可以使用:
运算符来执行基于正则表达式的匹配。1
2expr "Hello World" : '.*' # 输出 11
expr "Hello World" : '\(.*\)' # 输出 Hello World在大多数
expr
版本中,正则表达式匹配返回的是捕获的字符串的长度。但是,如果模式包含了括号,它通常返回括号内匹配到的字符串,而不是长度。
运算方法
-
内置的 Shell 算术扩展 (
$((...))
)1
2result=$((3 + 5))
echo $result # 输出 8 -
使用
let
命令1
2let result=3+5
echo $result # 输出 8 -
使用
expr
命令1
2result=$(expr 3 + 5)
echo $result # 输出 8 -
使用
bc
(基础计算器)1
2result=$(echo "3+5" | bc)
echo $result # 输出 8 -
使用
awk
进行计算1
2result=$(awk 'BEGIN {print 3+5}')
echo $result # 输出 8
-
总结
$((...))
是一种内置的 shell 算术扩展,适合进行整数运算。let
命令也是一个内置的 shell 命令,它同样用于执行整数运算。expr
是一个外部程序,可以处理整数和字符串运算,但在使用时需要注意空格和字符的转义。bc
是一个精度任意的计算器语言,可以进行浮点数运算。awk
是一个功能强大的文本处理工具,它内置了支持浮点运算的算术操作。
运算分类
-
整数运算
-
简介
- Shell 可以直接执行整数的加、减、乘、除等基本算术运算。例如使用
$(( ... ))
扩展或let
命令。 - 请注意,在使用乘法运算符时,如果您直接在命令行或脚本中输入命令,星号
*
可能需要转义或用引号括起来,以避免它被解释为通配符。但在$(( ... ))
算术扩展中,通常不需要转义。 - 另外,Shell 的除法是整数除法,如果除不尽,小数部分会被舍弃。如果您需要进行浮点运算,就需要使用
bc
或awk
等工具。
- Shell 可以直接执行整数的加、减、乘、除等基本算术运算。例如使用
-
加法(+)
1
echo $(( 5 + 3 )) # 输出 8
-
减法(-)
1
echo $(( 5 - 3 )) # 输出 2
-
乘法(*)
1
echo $(( 5 * 3 )) # 输出 15
-
除法(/)
1
echo $(( 15 / 3 )) # 输出 5
-
求余(%)
1
echo $((10 % 3)) # 输出 1
-
-
浮点运算
-
简介
- Shell 本身不支持浮点运算,但可以通过调用像
bc
或awk
这样的外部程序来执行浮点数学运算。 - 在使用
bc
时,scale
设置了小数点后的精度。如果不设置scale
,bc
默认的小数精度是 0,即执行整数除法。 awk
默认对浮点数具有内置的支持,并且会根据计算的结果自动设置精度。- 这些命令可以在 Linux shell 中直接执行,也可以在 shell 脚本中使用。 使用
echo
命令将运算表达式传给bc
是因为bc
是一个交互式的计算器程序,它从标准输入读取表达式。相比之下,awk
是一个模式扫描和文本处理语言,它的BEGIN
块在处理任何实际的文本输入之前执行,并直接输出计算结果。
- Shell 本身不支持浮点运算,但可以通过调用像
-
加法(+)
bc
1
echo "scale=2; 5.75 + 2.50" | bc # 输出 8.25
awk
1
awk 'BEGIN {print 5.75 + 2.50}' # 输出 8.25
-
减法(-)
bc
1
echo "scale=2; 5.75 - 2.50" | bc # 输出 3.25
awk
1
awk 'BEGIN {print 5.75 - 2.50}' # 输出 3.25
-
乘法(*)
bc
1
echo "scale=2; 5.75 * 2.50" | bc # 输出 14.37
awk
1
awk 'BEGIN {print 5.75 * 2.50}' # 输出 14.375
-
除法(/)
bc
1
echo "scale=2; 5.75 / 2.50" | bc # 输出 2.30
awk
1
awk 'BEGIN {print 5.75 / 2.50}' # 输出 2.3
-
-
比较运算
-
数值比较
-
等于:
-eq
1
2if [ 10 -eq 10 ]; then echo "Equal"; fi
# 输出:Equal -
不等于:
-ne
1
2if [ 10 -ne 20 ]; then echo "Not Equal"; fi
# 输出:Not Equal -
小于:
-lt
1
2if [ 5 -lt 10 ]; then echo "Less Than"; fi
# 输出:Less Than -
大于:
-gt
1
2if [ 20 -gt 10 ]; then echo "Greater Than"; fi
# 输出:Greater Than -
小于或等于:
-le
1
2if [ 10 -le 10 ]; then echo "Less Than or Equal"; fi
# 输出:Less Than or Equal -
大于或等于:
-ge
1
2if [ 10 -ge 5 ]; then echo "Greater Than or Equal"; fi
# 输出:Greater Than or Equal
-
-
字符串比较
-
字符串相等:
=
1
2if [ "hello" = "hello" ]; then echo "Strings are equal"; fi
# 输出:Strings are equal -
字符串不相等:
!=
1
2if [ "hello" != "world" ]; then echo "Strings are not equal"; fi
# 输出:Strings are not equal -
字符串为空:
-z
1
2
3str=""
if [ -z "$str" ]; then echo "String is empty"; fi
# 输出:String is empty-z
测试操作符用来检查字符串是否为空(Zero length)。如果给定的字符串长度为零,则返回真(true) -
字符串非空:
-n
1
2
3str="hello"
if [ -n "$str" ]; then echo "String is not empty"; fi
# 输出:String is not empty-n
测试操作符用来检查字符串是否非空(Non-zero length)。如果给定的字符串长度非零,则返回真(true)。 -
字符串小于:
<
1
2if [[ "abc" < "def" ]]; then echo "abc comes before def"; fi
# 输出:abc comes before def -
字符串大于:
>
1
2if [[ "def" > "abc" ]]; then echo "def comes after abc"; fi
# 输出:def comes after abc -
注意:
在进行字符串比较时,
<
和>
需要在[[ ]]
中使用,或者在[ ]
中使用时需要转义,如\<
和\>
,以避免被 shell 当作重定向操作。
-
-
-
逻辑运算
-
简介
- Shell 支持逻辑运算符,包括逻辑与(
&&
)、逻辑或(||
)和逻辑非(!
)。 - 在 Linux shell 中,可以使用
-a
和-o
进行逻辑运算,也可以使用&&
和||
进行条件组合。 - 注意,
-a
和-o
在test
或[ .. ]
命令中用于逻辑运算,但已经被认为是过时的,而且在 POSIX 标准中不鼓励使用。在现代 shell 脚本中,建议使用[[ .. ]]
结构与&&
和||
运算符。 - 同时请注意,
&&
和||
在 shell 中有双重含义。它们既可以用作逻辑运算符,也可以用作控制运算符,用于根据之前命令的退出状态来执行命令。在这里,我们主要讨论逻辑运算的使用。
- Shell 支持逻辑运算符,包括逻辑与(
-
逻辑与 (&&)
-
如果两个条件都为真,整个表达式为真。
1
[[ 3 -lt 5 && 7 -gt 2 ]] && echo "Both conditions are true" # 输出 "Both conditions are true"
检查 3 是否小于 5 并且同时 7 是否大于 2。因为两个条件都是真的,所以
echo
命令被执行。 -
在一行中使用
&&
来连续执行命令,仅当第一个命令成功时,第二个命令才会执行:1
2# 更新软件包列表并且安装一个新的软件包(仅当第一个命令成功时)
sudo apt update && sudo apt install -y package-name
-
-
逻辑或 (||)
-
如果两个条件中至少有一个为真,整个表达式为真。
1
[[ 3 -gt 5 || 7 -gt 2 ]] && echo "At least one condition is true" # 输出 "At least one condition is true"
检查 3 是否大于 5 或者 7 是否大于 2。在这个案例中,第一个条件是假的,但第二个条件是真的,所以
echo
命令被执行。 -
在一行中使用
||
来尝试执行第一个命令,如果它失败了,则执行第二个命令:1
2# 尝试先创建一个目录,如果失败了(目录已存在),则打印一个消息
mkdir /path/to/dir || echo "Directory already exists"
-
-
逻辑非 (!)
-
如果条件为假,则整个表达式为真。
1
[[ ! 3 -gt 5 ]] && echo "Condition is not true" # 输出 "Condition is not true"
检查 3 是否不大于 5。因为 3 不大于 5,逻辑非将这个假条件转换为真,所以
echo
命令被执行。 -
使用
!
来反转一个命令的退出状态:1
2# 如果文件不存在,则创建它
[ ! -e /path/to/file ] && touch /path/to/file
-
-
组合使用
布尔运算符可以组合使用来构造复杂的条件表达式
1
2
3
4# 检查文件是否不存在或者不可写
if [ ! -e /path/to/file ] || [ ! -w /path/to/file ]; then
echo "File does not exist or is not writable"
fi这些基本的布尔逻辑可以帮助你构建更复杂的脚本逻辑,以处理各种条件和情况。记得,为了避免执行逻辑可能不符合预期的情况,使用括号
(
和)
来明确表达式的优先级是一个好习惯。在 shell 中,你需要对括号使用转义字符,像这样\(
和\)
,或者将它们放在单引号内,像这样'( )'
。
-
-
位运算
-
简介
- Shell 支持位运算符,如位与(
&
)、位或(|
)、位非(~
)、位异或(^
)、位左移(<<
)和位右移(>>
)。 - 在 Linux shell 中,您可以使用
$(( ... ))
算术扩展来执行位运算。 - 在下述位非运算的例子中,结果可能会让人感到意外。在 Bash 中,数字默认被看作是有符号整数。位非运算会将数字的所有位取反。由于大多数 Bash 实现使用的是补码形式表示负数,所以在对 5 进行位非运算后,我们得到了 -6 而不是一个大正数。如果你对一个 8 位的无符号整数进行位非运算,结果会是 250 (二进制:11111010),这是因为全部的位从 00000101 变成了 11111010。但在有符号的环境中,最高位(符号位)为 1 表示这是一个负数,具体的数值需要根据补码规则来解释。
- Shell 支持位运算符,如位与(
-
位与(AND)运算符
&
1
echo $(( 5 & 3 )) # 输出 1 (二进制: 101 & 011 = 001)
-
位或(OR)运算符
|
1
echo $(( 5 | 3 )) # 输出 7 (二进制: 101 | 011 = 111)
-
位非(NOT)运算符
~
1
echo $(( ~5 )) # 输出 -6 (二进制: ~101 = ...1111111111111010, 为二进制补码形式)
-
位异或(XOR)运算符
^
1
echo $(( 5 ^ 3 )) # 输出 6 (二进制: 101 ^ 011 = 110)
-
位左移(LEFT SHIFT)运算符
<<
1
echo $(( 5 << 1 )) # 输出 10 (二进制: 101 << 1 = 1010)
-
位右移(RIGHT SHIFT)运算符
>>
1
echo $(( 5 >> 1 )) # 输出 2 (二进制: 101 >> 1 = 010)
-
-
文件测试运算
-
检查文件是否存在:
-e
1
2
3if [ -e /path/to/file ]; then
echo "File exists."
fi -
检查文件是否是常规文件:
-f
1
2
3if [ -f /path/to/file ]; then
echo "This is a regular file."
fi -
检查文件是否是目录:
-d
1
2
3if [ -d /path/to/dir ]; then
echo "This is a directory."
fi -
检查文件是否可读:
-r
1
2
3if [ -r /path/to/file ]; then
echo "File is readable."
fi -
检查文件是否可写:
-w
1
2
3if [ -w /path/to/file ]; then
echo "File is writable."
fi -
检查文件是否可执行:
-x
1
2
3if [ -x /path/to/file ]; then
echo "File is executable."
fi -
检查文件是否为空:
-s
1
2
3
4
5if [ -s /path/to/file ]; then
echo "File is not empty."
else
echo "File is empty."
fi -
检查文件是否是符号链接:
-L
1
2
3if [ -L /path/to/link ]; then
echo "This is a symbolic link."
fi- 符号链接(symbolic link)是一种特殊类型的文件,它包含对另一个文件或目录的引用。当访问一个符号链接时,操作系统会自动将该访问重定向到链接指向的目标文件或目录。
- 符号链接在概念上类似于 Windows 系统中的快捷方式,但在 Unix 和类 Unix 系统中,符号链接是文件系统级别的实体。
- 符号链接有两种类型:
- 软链接(或称为符号链接): 这是最常见的类型,创建时使用
ln -s
命令。软链接可以跨文件系统,并且可以链接到目录。 - 硬链接: 硬链接是对文件系统中文件的另一个引用,创建时使用
ln
命令。硬链接不能跨越文件系统,也不能链接到目录。
- 软链接(或称为符号链接): 这是最常见的类型,创建时使用
-
检查文件是否最近被访问过:
-a
1
2
3if [ -a /path/to/file ]; then
echo "File has been accessed recently."
fi注意:
-a
在某些环境中可能是检查文件是否存在的老旧方法,这里的“访问”是指使用touch
命令或者类似的方式更新文件的访问时间。 -
检查一个文件是否比另一个文件新:
-nt
1
2
3if [ /path/to/file1 -nt /path/to/file2 ]; then
echo "file1 is newer than file2."
fi -
检查一个文件是否比另一个文件旧:
-ot
1
2
3if [ /path/to/file1 -ot /path/to/file2 ]; then
echo "file1 is older than file2."
fi -
检查一个文件是否为字符设备文件:
-c
字符设备文件是一种支持以字符为单位的顺序访问的设备文件。它们通常用于像键盘、鼠标或其他类型的字符流设备。
1
2
3if [ -c /dev/tty ]; then
echo "/dev/tty is a character device file."
fi -
检查一个文件是否为块设备文件:
-b
块设备文件是一种支持随机访问的设备文件。对于块设备文件的每次读写操作都以块为单位进行,例如硬盘驱动器、光盘驱动器等。
1
2
3if [ -b /dev/sda ]; then
echo "/dev/sda is a block device file."
fi
-
test 命令
-
简介
在 Linux 中,
test
命令是一个检查文件类型和比较值的工具。它用于评估条件表达式。如果表达式为真,则test
命令退出状态为 0(表示成功)。如果表达式为假,则退出状态为 1(表示失败)。除了直接使用test
命令,还有一个[
的同义命令,它们的行为是一样的,但是使用[
的时候需要注意,命令的末尾必须有一个相应的]
。 -
文件属性测试
- 检查文件是否存在:
test -e filename
- 检查文件是否是目录:
test -d filename
- 检查文件是否可读:
test -r filename
- 检查文件是否可写:
test -w filename
- 检查文件是否可执行:
test -x filename
- 检查文件是否是普通文件:
test -f filename
- 检查文件是否是符号链接:
test -L filename
- 检查文件是否大小大于零:
test -s filename
- 检查文件是否存在:
-
字符串比较测试
- 检查字符串是否为空:
test -z string
- 检查字符串是否非空:
test -n string
- 检查两个字符串是否相等:
test "string1" = "string2"
- 检查两个字符串是否不相等:
test "string1" != "string2"
- 检查字符串是否为空:
-
整数比较测试
- 检查两个数是否相等:
test num1 -eq num2
- 检查第一个数是否大于第二个数:
test num1 -gt num2
- 检查第一个数是否小于第二个数:
test num1 -lt num2
- 检查第一个数是否大于等于第二个数:
test num1 -ge num2
- 检查第一个数是否小于等于第二个数:
test num1 -le num2
- 检查两个数是否不相等:
test num1 -ne num2
- 检查两个数是否相等:
-
组合条件测试
- 逻辑与:
test condition1 -a condition2
- 逻辑或:
test condition1 -o condition2
- 逻辑非:
test ! condition
- 逻辑与:
-
示例
-
在 shell 脚本中,通常会在
if
语句中使用test
命令,例如:1
2
3
4
5if test -f "/path/to/somefile"; then
echo "File exists."
else
echo "File does not exist."
fi -
或者使用
[
的同义命令形式:1
2
3
4
5if [ -f "/path/to/somefile" ]; then
echo "File exists."
else
echo "File does not exist."
fi注意:使用
[
时,在[
和]
之间以及它们与表达式之间都应该有空格。
-
分支结构
-
if 语句
-
if
语句用来基于一定条件执行命令,常见的形式为:1
2
3
4
5
6
7if [ condition ]; then
# 命令
elif [ another_condition ]; then
# 其他命令
else
# 默认命令
fi -
例如,检查用户输入的数字是否为正数、负数或零:
1
2
3
4
5
6
7
8read -p "Enter a number: " num
if [ "$num" -gt 0 ]; then
echo "The number is positive."
elif [ "$num" -lt 0 ]; then
echo "The number is negative."
else
echo "The number is zero."
fi- 在 Bash shell 中,
read
命令用于从标准输入(通常是键盘)读取一行数据。当你在read
命令中使用-p
选项时,它允许你指定一个提示字符串,用于在读取输入之前显示给用户。 - 这里的
-p
选项后面跟随的字符串 "Enter a number: " 会在命令行上显示出来,并立即等待用户输入。用户输入的值将被存储到变量num
中,然后可以在脚本中使用这个变量。
- 在 Bash shell 中,
-
-
case 语句
-
case
语句用于基于不同的值执行不同命令,语法如下:1
2
3
4
5
6
7
8
9
10
11case $variable in
pattern1)
# 命令
;;
pattern2)
# 命令
;;
*)
# 默认命令
;;
esac -
例如,根据用户输入的字符进行不同的问候:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15read -p "Enter a letter (a/b/c): " letter
case $letter in
a)
echo "Hello, Apple!"
;;
b)
echo "Hello, Banana!"
;;
c)
echo "Hello, Cherry!"
;;
*)
echo "I don't know this letter."
;;
esac
-
-
select 语句
select
语句用于生成简单的菜单,它基本上是case
语句的一种特殊形式,常用于交互式脚本。select
语句为用户提供一个简单的菜单选择机制,用户可以从一系列选项中选择一个。当执行select
语句时,它会创建一个菜单并提示用户进行选择。用户输入一个数字来选择与该数字相关联的选项。用户的选择将赋值给变量,然后可以在脚本的其他部分使用该变量。- 一旦
break
命令执行,控制流程将退出select
循环,继续执行后面的命令。如果没有break
命令,select
语句将无限循环,不断提示用户选择。语法如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17select item in option1 option2 option3; do
case $item in
option1)
echo "You picked $item."
;;
option2)
echo "You picked $item."
;;
option3)
echo "You picked $item."
;;
*)
echo "Invalid option."
break
;;
esac
done - 例如,创建一个简单的菜单让用户选择:输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20echo "Please pick an option:"
select fruit in Apple Banana Cherry Quit; do
case $fruit in
Apple)
echo "Apples are delicious!"
;;
Banana)
echo "Bananas are full of potassium!"
;;
Cherry)
echo "Cherries are sweet!"
;;
Quit)
break
;;
*)
echo "Please choose a valid option."
;;
esac
done1
2
3
4
5
6Please pick an option:
1) Apple
2) Banana
3) Cherry
4) Quit
#?
循环结构
-
for 循环
for
循环通常用于遍历列表中的每一个元素。- 示例 1:数值范围
1
2
3for i in {1..5}; do
echo "Number $i"
done这将打印数字1到5。
- 示例 2:遍历列表
1
2
3for fruit in apple banana cherry; do
echo "I like $fruit!"
done这将遍历列表中的每种水果,并打印出相应的语句。
-
while 循环
while
循环会在指定的条件为真时不断执行一组命令。- 示例:
1
2
3
4
5counter=1
while [ $counter -le 5 ]; do
echo "Counter is $counter"
((counter++))
done这里只要
counter
的值小于或等于5,循环就会继续执行,打印出counter
的值,并将counter
的值加1。
-
until 循环
until
循环则是当指定条件为假时执行,一旦条件为真,循环停止。- 示例:
1
2
3
4
5counter=1
until [ $counter -gt 5 ]; do
echo "Counter is $counter"
((counter++))
done这个循环会一直执行直到
counter
的值大于5。
-
C 风格的 for 循环
- Bash 也支持 C 程序设计语言风格的 for 循环。
- 示例:
1
2
3for ((i = 0; i <= 5; i++)); do
echo "Number $i"
done这将按照 C 语言风格递增
i
的值,并打印出来,直到它大于 5。
-
break 和 continue
-
简介
- 在 Linux shell 中,
break
和continue
是用于控制循环流程的命令,它们可以用在for
、while
或until
循环中。 - 这两个命令的作用是对循环的执行流程进行控制,使得脚本编写者可以更精细地控制循环的行为。
- 在 Linux shell 中,
-
break
break
命令用于立即退出包含它的循环,即使循环的结束条件尚未满足。如果循环嵌套,break
只会退出最内层的循环。1
2
3
4
5
6for i in {1..10}; do
if [ $i -eq 5 ]; then
break
fi
echo "Number $i"
done- 这段代码会打印数字 1 到 4。当
i
等于 5 时,break
命令执行,导致for
循环提前结束。
-
continue
continue
命令用于跳过当前循环的剩余部分,并继续执行下一个循环迭代。1
2
3
4
5
6for i in {1..10}; do
if [ $i -eq 5 ]; then
continue
fi
echo "Number $i"
done- 这段代码会打印数字 1 到 10,但是不会打印数字 5。当
i
等于 5 时,continue
命令执行,导致for
循环跳过数字 5 直接继续到数字 6。
-
函数
- 在 Linux shell 脚本中,函数是一种复用代码的方式,可以在脚本的任意位置调用以执行特定的任务。下面是一个简单的函数示例:
1
2
3
4
5
6
7
8
9
10
11
12
# 定义一个名为 greet 的函数
greet() {
echo "Hello, $1!"
}
# 调用函数 greet 并传递一个参数
greet "World"
# 再次调用函数 greet 并传递一个不同的参数
greet "User"- 在这个例子中,我们定义了一个名为
greet
的函数,这个函数接受一个参数(通过$1
访问),当函数被调用时,会打印出 "Hello, " 后跟传递给它的参数。 - 函数
greet
被调用了两次,第一次传递了 “World” 作为参数,第二次传递了 “User”。这会产生以下输出:1
2Hello, World!
Hello, User!
- 在这个例子中,我们定义了一个名为
- 函数也可以接收多个参数,并在函数体内部通过
$1
,$2
,$3
等访问这些参数。如果你想访问所有的参数,可以使用$@
或$*
。函数可以通过return
命令返回状态码(不是值),或者通过echo
命令输出结果,然后在函数外部通过命令替换捕获这些输出。 - 下面是一个更复杂的函数例子,该函数用于计算两个数的和:
1
2
3
4
5
6
7
8
9
10
11
12
13
# 定义一个名为 add 的函数,计算两个数的和
add() {
local sum=$(($1 + $2))
echo $sum
}
# 调用函数,并捕获输出(函数的返回值)
result=$(add 3 5)
# 打印结果
echo "The sum is: $result"- 这个脚本定义了一个
add
函数,接收两个参数(两个数),然后在局部变量sum
中计算它们的和。函数通过echo
输出计算结果,该结果可以在函数外部通过命令替换($(add 3 5)
)来捕获。运行这个脚本会产生以下输出:1
The sum is: 8
- 这个脚本定义了一个
- 在 Linux shell 脚本中,
$?
是一个特殊的变量,它表示最后执行的命令的退出状态。退出状态是一个从 0 到 255 的数值,其中 0 通常表示成功,而非零值通常表示错误或异常状态。1
2
3
4
5
6
7
8
9
10
11
12
13
arg() {
echo $1
echo $2
echo $#
echo $@
echo $*
echo "$*"
return 123
}
arg 1 2
echo $?echo $1
打印出第一个参数,即1
。echo $2
打印出第二个参数,即2
。echo $#
打印出传递给函数的参数个数,这里是2
。echo $@
打印出所有传递给函数的参数,这里是1 2
。它们是按照传递给函数的原样输出的。echo $*
与$@
类似,也打印出所有传递给函数的参数,这里也是1 2
。不过在被双引号包围时,"$*"
把所有参数看作一个整体,而"$@"
仍然把它们视为独立的参数。return 123
设置函数的退出状态为123
。在 bash 中,函数的退出状态可以通过$?
获取,类似于一个程序或命令的退出状态。
- 当你在函数中或函数之后使用
$?
,你会得到最近执行的命令(这可以是函数调用或任何其他命令)的退出状态。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 定义一个函数,它尝试使用一个不存在的命令
fakeCommand() {
non_existing_command
}
# 调用函数 fakeCommand
fakeCommand
# 打印 fakeCommand 函数后的退出状态
echo "The exit status of the function was: $?"
# 现在执行一个成功的命令
ls >/dev/null
# 打印 ls 命令后的退出状态
echo "The exit status of 'ls' was: $?"- 在此脚本中,
fakeCommand
函数尝试执行一个不存在的命令。该命令将失败,并返回一个非零退出状态。当我们打印$?
时,它将显示那个失败的退出状态。 - 随后,我们执行
ls
命令并将输出重定向到/dev/null
(忽略输出)。因为ls
命令通常会成功运行,所以紧接着该命令后打印的$?
应该是 0,表示成功。 - 运行这个脚本会产生以下输出(假设脚本名称为
test.sh
):1
2
3./test.sh: line 5: non_existing_command: command not found
The exit status of the function was: 127
The exit status of 'ls' was: 0
- 在此脚本中,
服务管理
-
简介
- Linux 系统通常使用系统服务守护进程(如 Systemd、SysVinit 或 Upstart)来管理服务。以 Systemd 为例,它是目前大多数 Linux 发行版使用的初始化系统和服务管理器。
- 请注意,对于大多数服务管理命令,你需要具有管理员权限,因此在下述示例中使用了
sudo
命令。此外,服务名称可能因不同的 Linux 发行版而异,如sshd
服务在某些发行版中可能被称为ssh.service
。
-
管理服务
以下是一些基本的 Systemd 命令,用于管理服务:
-
启动服务
1
sudo systemctl start [service_name]
例如,要启动 SSH 服务,使用:
1
sudo systemctl start sshd
-
停止服务
1
sudo systemctl stop [service_name]
例如,停止 SSH 服务:
1
sudo systemctl stop sshd
-
重启服务
1
sudo systemctl restart [service_name]
重启 SSH 服务的命令如下:
1
sudo systemctl restart sshd
-
查看服务状态
1
sudo systemctl status [service_name]
查看 SSH 服务的状态:
1
sudo systemctl status sshd
-
使服务在启动时自动运行
1
sudo systemctl enable [service_name]
使 SSH 服务开机自启:
1
sudo systemctl enable sshd
-
禁用服务自动启动
1
sudo systemctl disable [service_name]
禁止 SSH 服务开机自启:
1
sudo systemctl disable sshd
-
重载服务配置
1
sudo systemctl reload [service_name]
如果服务支持不中断服务进行配置重载,那么可以用此命令。例如,对 Apache 服务进行配置文件的重载:
1
sudo systemctl reload apache2
-
查看所有服务的状态
1
systemctl list-units --type service
这将列出所有系统服务及其状态。
-
查看已启用的服务
1
systemctl list-unit-files --type=service --state=enabled
这显示了所有设置为开机自启的服务。
-
检查服务是否启动
1
systemctl is-active [service_name]
例如,检查 SSH 服务是否运行中:
1
systemctl is-active sshd
-
-
自建服务
-
简介
- 在 Linux 中创建自己的服务通常意味着编写一个 systemd 服务单元文件。
- 确保在实际部署之前,在一个安全的环境中测试你的服务脚本。任何时候,如果服务脚本或单元文件需要更新,都需要重新加载 systemd 守护进程,并重启服务以使更改生效。
-
创建服务单元文件
- 编写一个服务单元文件:首先,你需要创建一个服务单元文件,通常放在
/etc/systemd/system/
目录下。文件通常以.service
结尾。以下是一个基本的服务单元文件示例:这个服务单元文件定义了一个服务,它将在1
2
3
4
5
6
7
8
9
10
11
12[Unit]
Description=My Custom Service
After=network.target
[Service]
ExecStart=/usr/local/bin/my-custom-script.sh
Restart=on-failure
User=nobody
Group=nogroup
[Install]
WantedBy=multi-user.targetnetwork.target
之后启动,运行位于/usr/local/bin/
的脚本,并在失败时自动重启,以nobody
用户的身份运行。 - 编写你的脚本:你需要创建并编辑
/usr/local/bin/my-custom-script.sh
文件,将以下内容加入脚本:记得给脚本执行权限:1
2
3
4
5
6
7
# My custom script
while true; do
# 你的逻辑代码
sleep 60
done1
sudo chmod +x /usr/local/bin/my-custom-script.sh
- 编写一个服务单元文件:首先,你需要创建一个服务单元文件,通常放在
-
重新加载 systemd 配置
使用以下命令让 systemd 重新加载所有配置,使得新服务生效:
1
sudo systemctl daemon-reload
-
启动服务
接下来,使用以下命令启动你的服务:
1
sudo systemctl start my-custom-service
这里的
my-custom-service
应该与你的.service
文件的文件名相匹配。 -
查看服务状态
使用以下命令检查服务的状态,确保它正在运行:
1
sudo systemctl status my-custom-service
-
使服务开机自启
如果你的服务工作正常,你可能希望它在系统启动时自动启动:
1
sudo systemctl enable my-custom-service
-
测试服务的持久性
重启计算机并检查你的服务是否能够成功启动:
1
sudo systemctl status my-custom-service
-
日志查看
如果服务没有按预期工作,你可以检查服务的日志以获取更多信息:
1
sudo journalctl -u my-custom-service
这将显示与你的服务相关的所有日志。
-
-
service
命令- 在较旧的 Linux 系统中,或者在仍然使用 SysVinit 或 Upstart 的系统中,服务是通过
service
命令或直接调用/etc/init.d/
脚本来管理的。然而,在使用 Systemd 的系统中,推荐的方法是使用systemctl
命令,因为 Systemd 已经取代了这些较旧的方法成为大多数现代 Linux 发行版的默认初始化系统和服务管理器。 - 如果你尝试在一个使用 Systemd 的系统上使用
service
命令,它通常会被重定向到systemctl
命令,以保持兼容性。例如,当你运行:1
sudo service my-custom-service start
- 在后台,它可能会被转换为:
1
sudo systemctl start my-custom-service
- 如果你所在的系统确实是使用 Systemd 的话,最好直接使用
systemctl
命令,因为这将保证与系统的服务管理工具直接对接,同时也拥有更多的功能和更好的支持。 - 不过,如果你的系统确实是使用 SysVinit 或 Upstart 的,那么你将需要:
- 将服务脚本放在
/etc/init.d/
目录下。 - 使脚本可执行:
sudo chmod +x /etc/init.d/my-custom-service
- 使用
update-rc.d
或类似工具来配置脚本的自动启动。 - 之后就可以使用
service
命令来管理服务了。
- 将服务脚本放在
- 但在现代的 Linux 发行版中,这已经变得不太常见了,Systemd 是大多数发行版的标准。
- 在较旧的 Linux 系统中,或者在仍然使用 SysVinit 或 Upstart 的系统中,服务是通过