Shell基础
一、Shell概述
- 什么是Shell:
- Shell是用户与操作系统内核之间的接口,允许用户通过命令行或脚本来与操作系统进行交互。
- 它解释用户输入的命令,并将其转换为操作系统能够理解的指令,然后执行这些指令并将结果返回给用户。
- 种类:
- Linux系统中有多种Shell可供选择,其中最常见的是Bash(Bourne Again Shell)。
- 其他常见的Shell包括C Shell(csh),Korn Shell(ksh),Z Shell(zsh)等。
- 功能:
- 命令行界面:提供一个命令行界面,用户可以通过键盘输入命令并与操作系统进行交互。
- 脚本编程:支持编写脚本来批处理一系列命令,实现自动化任务执行。
- 环境配置:允许用户自定义Shell环境,包括设置环境变量、定义别名、配置提示符等。
- 管道和重定向:支持管道和重定向操作,使用户能够更有效地处理数据流和进行文件操作。
- 权限管理:提供管理系统权限的方式,允许执行需要特殊权限的命令以及控制文件和系统资源的访问权限。
- 应用场景:
- 日常系统管理:执行文件操作、系统配置等任务。
- 脚本编程:自动化任务执行,批量处理数据等。
- 系统维护和监控:管理进程、查看系统日志等。
- 重要性:
- Shell在Linux系统中扮演着至关重要的角色,是用户管理和操作系统资源的主要工具之一。
- 它提供了强大的交互和编程能力,使用户能够更有效地执行各种任务并控制系统行为。
二、常见的shell解释器
1、Bash Shell
- **Bash(Bourne Again Shell)**是一种基于GNU项目开发的Shell,是当前大多数Linux发行版中默认的Shell。
- 特性:
- 支持快捷键:例如,Ctrl+C用于强制终止进程,Ctrl+L用于清屏,Tab键用于命令补全等。
- 支持查看历史命令(history):用户可以通过history命令查看之前执行过的命令,并且可以使用上下箭头键在历史命令之间切换。
- 支持别名(alias):用户可以通过alias命令为常用命令设置别名,以简化输入。
- 标准输入和标准输出的重定向:用户可以使用重定向符号(>、<)将命令的输入和输出重定向到文件或其他命令中。
- 管道符:允许用户将一个命令的输出作为另一个命令的输入,以实现命令之间的数据传递。
- 文件名通配机制:支持通配符(例如,*和?)来匹配文件名,以便进行文件操作。
- 支持命令hash:可以通过hash命令来加快命令的执行速度,以减少命令搜索的时间。
- 支持变量:用户可以定义和使用变量,包括环境变量和用户自定义变量,以在Shell脚本中存储和操作数据。
2、其他信息
-
用户可以使用
cat /etc/shells
命令来查看系统中可用的Shell种类。 -
Bash提供了丰富的内置命令和功能,使得用户能够更方便地进行系统管理和脚本编写。
-
由于其广泛应用和强大功能,Bash已成为Linux和Unix系统中最受欢迎的Shell之一,为用户提供了灵活和强大的命令行环境。
三、shell脚本编写规范与执行方法
1、编写规范:
-
文件命名:
- 以
.sh
结尾,例如myscript.sh
。
- 以
-
文件头部:
- 使用Shebang(
#!
)指定解释器,如#!/bin/bash
。
- 使用Shebang(
-
注释:
- 清晰解释脚本功能、变量和命令用途。
-
缩进和格式化:
- 保持一致性,通常使用4个空格进行缩进。
-
变量命名:
- 描述性强,使用小写字母和下划线,如
my_variable
。
- 描述性强,使用小写字母和下划线,如
-
引号使用:
- 引用变量或包含空格的字符串时使用双引号,如
"$my_variable"
。
- 引用变量或包含空格的字符串时使用双引号,如
-
错误处理:
- 包含错误处理机制,检查命令执行结果并采取适当处理。
-
退出状态码:
- 返回合适的退出状态码,0表示成功,非0表示失败。
-
可重用性:
- 将代码模块化,提高脚本的可重用性和可维护性。
2、执行方法:
-
赋予执行权限:
- 使用
chmod +x myscript.sh
为脚本文件添加执行权限。
- 使用
-
直接执行:
- 在命令行中输入脚本文件的路径,例如
./myscript.sh
,这要求脚本文件有执行权限。
- 在命令行中输入脚本文件的路径,例如
-
作为参数传递给Shell解释器:
- 使用指定的Shell解释器执行脚本,例如
bash myscript.sh
或sh myscript.sh
。这种方式可以确保脚本使用指定的解释器执行,而不受系统默认解释器的影响。
- 使用指定的Shell解释器执行脚本,例如
-
在当前Shell环境中执行:
- 使用
.
(点号)或source
命令,在当前Shell环境中执行脚本,例如. myscript.sh
或source myscript.sh
。这种方式会在当前Shell进程中执行脚本,使得其中定义的变量和函数在当前Shell环境中可用。
- 使用
-
在其他Shell环境中执行:
- 将脚本文件名作为参数传递给其他Shell解释器执行,例如
zsh myscript.sh
。这种方式适用于在不同的Shell环境中执行脚本,如使用zsh解释器执行Bash脚本。
- 将脚本文件名作为参数传递给其他Shell解释器执行,例如
-
通过绝对路径执行:
- 可以使用脚本文件的绝对路径来执行脚本,例如
/path/to/myscript.sh
。这种方式可以确保在任何目录下都可以执行脚本。
- 可以使用脚本文件的绝对路径来执行脚本,例如
-
通过别名执行:
- 可以通过为脚本文件设置别名来执行脚本,例如
alias runmyscript='/path/to/myscript.sh'
,然后使用runmyscript
命令来执行脚本。
- 可以通过为脚本文件设置别名来执行脚本,例如
四、shell脚本执行的错误排查方法
bash -n 脚本名称 #只检查语法错误,不真正执行脚本。定位的错误行可能不准确。
bash -x 脚本名称 #显示每个命令的执行过程,方便发现逻辑错误
五、交互式硬件设备的介绍
1、介绍
- 交互式硬件设备是指能够与用户进行实时交互并响应用户输入的物理设备。这些设备通常与计算机系统或其他电子设备连接,通过输入和输出接口与用户进行信息交流。以下是一些常见的交互式硬件设备:
-
键盘:
- 键盘是最常见的交互式输入设备之一,用户可以通过键盘输入文本、命令、数字等信息。键盘通常具有标准布局,包括字母、数字、符号键等,同时还可能包括专用功能键和快捷键。
-
鼠标:
- 鼠标是一种常见的交互式指针设备,用户可以通过移动鼠标来控制屏幕上的光标位置,并通过点击鼠标按钮执行操作。鼠标通常用于图形用户界面(GUI)中,提供直观的界面导航和操作功能。
-
触摸屏:
- 触摸屏是一种直接感应用户触摸操作的设备,用户可以通过手指或触控笔在屏幕上进行操作。触摸屏广泛应用于智能手机、平板电脑、信息亭、交互式显示屏等设备中,提供直观的触摸操作体验。
-
游戏手柄:
- 游戏手柄是专门用于电子游戏控制的交互式设备,通常包括按钮、摇杆、方向键等控制元素,用户可以通过手柄来操作游戏中的角色、车辆等。
-
触摸笔:
- 触摸笔是一种用于绘图、写字和精确操作的输入设备,通常用于平板电脑、数字绘图板等设备上。触摸笔可以提供更精准的操作和更自然的书写体验。
-
扫描仪:
- 扫描仪是一种用于将纸质文档、照片等转换为数字图像的设备,用户可以通过扫描仪将实体文件转换为计算机可处理的电子文件,实现数字化存储和处理。
-
语音识别设备:
- 语音识别设备可以将用户的口头语音指令转换为文本或操作命令,实现无需键盘或鼠标的语音交互操作。这种设备在智能助手、语音控制系统等应用中得到广泛应用。
这些交互式硬件设备使用户能够与计算机系统或其他电子设备进行直接、实时的交流和操作,极大地丰富了人机交互的方式和体验。
2、理解Unix/Linux系统中的标准I/O流"
-
标准输入 (stdin):
- 标准输入是指程序接收输入数据的默认来源。通常情况下,标准输入是从键盘接收用户输入的数据。例如,当你在终端中运行一个程序时,可以通过键盘输入数据,这些数据将会被程序读取并处理。
- 在Unix/Linux系统中,标准输入使用文件描述符
0
来表示。
-
标准输出 (stdout):
- 标准输出是指程序将输出数据发送的默认位置。通常情况下,标准输出是输出到终端(屏幕),供用户查看。例如,当程序执行时,可以通过标准输出将结果打印到终端上。
- 在Unix/Linux系统中,标准输出使用文件描述符
1
来表示。
-
标准错误输出 (stderr):
- 标准错误输出是指程序将错误信息发送的默认位置。通常情况下,标准错误输出也是输出到终端(屏幕),用于报告执行过程中出现的错误和异常情况。
- 与标准输出不同,标准错误输出专门用于报告错误信息,以便用户能够区分正常输出和错误输出。
- 在Unix/Linux系统中,标准错误输出使用文件描述符
2
来表示。
这些概念是Unix/Linux系统中标准I/O的基础,帮助程序实现与用户和其他程序之间的交互,并能够及时报告执行过程中出现的错误信息。
六、shell中的括号
1、小括号( )
(1)创建子shell
- 最常见的用法是使用圆括号来创建一个子shell,其中的命令将在子shell环境中执行。这意味着在子shell中执行的命令不会影响到父shell,而且在子shell中定义的变量和函数也不会影响到父shell中的变量和函数。
- 圆括号中的命令在执行完毕后,子shell将被销毁,所有在子shell中创建的临时变量和修改的环境都会在子shell销毁时消失。
- 这种机制常用于需要在临时环境中执行命令的情况,或者需要在子shell中进行变量修改而不影响父shell的情况。
执行命令序列
(pwd; ls -l; echo "Hello, World")
这个示例中,圆括号内包含了多个命令:pwd
、ls -l
和 echo "Hello, World"
。这些命令将会在一个子shell中依次执行。子shell执行完毕后,父shell的工作目录并不会改变,因为pwd
命令只在子shell中执行。
(2)在子shell中执行多个命令
- 圆括号内可以包含多个命令,这些命令会按顺序在子shell中执行。
- 圆括号中的多个命令可以用分号
;
分隔,也可以用换行符分隔。 - 这种机制允许在一个子shell中执行多个相关联的命令,以简化代码结构并提高可读性。
(3)在子shell中执行代码块
- 圆括号可以用于在子shell中创建一个代码块,这个代码块中的所有命令将在子shell中执行。
- 代码块中可以包含任意数量的命令,用于执行一系列相关联的操作。
(
for i in {1..5}; do
echo "Value of i: $i"
done
)
这个示例中,圆括号内部包含了一个代码块,其中包含一个简单的for
循环。该代码块将会在一个子shell中执行,并输出循环变量i
的值从1到5。
(4)影响变量作用域
- 圆括号内部的变量在子shell中定义的情况下,作用域仅限于子shell内部,不会影响到父shell中的同名变量。
- 这种机制可以有效地限制变量的作用域,防止变量污染和不必要的副作用。
2、中括号[ ]
-
用于条件测试,判断条件是否为真。
-
方括号内可以包含各种条件测试表达式,例如测试变量是否为空、两个字符串是否相等等。
-
这种测试通常在控制流结构中使用,如
if
语句和循环。 -
例如:
if [ "$variable" = "value" ]; then echo "Variable is equal to value" fi
-
中括号也可以用来定义数组
my_array=["value1" "value2" "value3"]
3、大括号{ }
-
用于创建命令组合或范围扩展。
-
花括号可以用于生成命令组合,也可以用于生成文件名范围。
-
适用于需要执行一系列相关命令或操作的情况,或者需要生成一系列相关文件名的情况。
-
例如:
mkdir {dir1,dir2,dir3} # 创建多个目录 cp file{1..3}.txt dest/ # 复制文件1.txt、2.txt、3.txt到dest目录中
-
文件重定向:大括号还可以用于文件重定向。
{ command1; command2; } > output.txt
4、< >尖括号
-
用于输入输出重定向。
-
尖括号可用于将命令的输入或输出重定向到文件或设备。
-
这在Shell脚本中常用于处理文件输入输出。
-
例如:
command > output.txt # 将命令的输出重定向到output.txt文件中 command < input.txt # 将input.txt文件内容作为命令的输入
重定向操作的符号用法
七、shell中的引号
- 在Shell脚本中,有三种常见的引号:单引号
'
、双引号"
和反引号`
。它们分别有不同的含义和用法:
1、单引号 '
- 在单引号中的文本被视为纯文本,不会进行任何解释或替换。单引号中的特殊字符(如变量、通配符、命令替换等)都会被当作普通字符对待。
- 单引号常用于保留字符串中的特殊字符,使其作为字面值传递给命令或变量。
- 例如:
echo 'Hello, $USER' # 输出:Hello, $USER
2、双引号 "
- 在双引号中的文本允许进行变量替换和转义字符替换,但不允许通配符扩展。
- 双引号中的变量会被替换为其对应的值,而不是字面字符串。同时,转义字符(如
\n
、\t
等)也会被解释为特殊字符。 - 双引号常用于拼接字符串、保留特殊字符的含义、以及将命令输出赋值给变量。
- 例如:
name="Alice" echo "Hello, $name" # 输出:Hello, Alice
3、反引号 `
- 反引号用于执行命令替换,即将反引号中的命令执行,并将其输出结果作为字符串返回。
- 反引号内的命令会被执行,并将其标准输出捕获到字符串中。
- 反引号通常用于获取命令的输出并将其赋值给变量,或者作为参数传递给其他命令。
- 例如:
date=`date` echo "Current date: $date"
这些引号在Shell脚本中用于控制字符串的解释和替换方式,使得脚本能够正确地处理变量、特殊字符和命令输出。
八、数值的运算方法
- 在Shell脚本中进行数值运算有几种方法,主要包括使用内置的算术扩展、使用
expr
命令、使用bc
命令以及使用其他外部工具。以下是每种方法的简要说明:
1、算术扩展
- Shell中的算术扩展可以通过双括号
(( ))
或者let
命令来执行数值运算。这种方法支持整数运算,并且可以直接使用变量名进行计算。 - 例如:
# 使用双括号 ((result = 5 + 3)) echo $result # 使用let命令 let "result = 5 + 3" echo $result
2、expr命令
expr
是一个用于字符串处理和数值运算的命令,它可以用于执行简单的数学运算,如加法、减法、乘法和除法。- 例如:
result=$(expr 5 + 3) echo $result
3、bc命令
bc
是一个用于任意精度数学运算的命令行计算器。它支持浮点数运算和高精度整数运算,并且提供了丰富的数学函数。- 例如:
result=$(echo "5 + 3" | bc) echo $result
4、echo $[ ]
和 echo $(( ))
echo $[ ]
和 echo $(( ))
都是用于执行算术运算的方法,它们的语法格式相似,可以用来进行整数的简单运算。
-
echo $[ ]
:使用方括号进行算术运算。这种语法已经逐渐被弃用,不推荐使用。 -
echo $(( ))
:使用双括号进行算术运算。这是一种更现代、更推荐的语法形式。
以下是这两种语法的示例以及支持的运算符:
-
使用方括号
$[ ]
进行算术运算的示例:echo $[5 + 3] # 输出:8 echo $[10 - 4] # 输出:6 echo $[3 * 2] # 输出:6 echo $[15 / 3] # 输出:5 echo $[17 % 4] # 输出:1
-
使用双括号
$(( ))
进行算术运算的示例:echo $((5 + 3)) # 输出:8 echo $((10 - 4)) # 输出:6 echo $((3 * 2)) # 输出:6 echo $((15 / 3)) # 输出:5 echo $((17 % 4)) # 输出:1
这些语法都支持基本的算术运算符,包括加法 +
、减法 -
、乘法 *
、除法 /
和取余 %
。通过这些语法,可以在Shell脚本中执行简单的整数运算,并将结果输出到标准输出。
5、其他工具
- 在Shell脚本中还可以使用其他外部工具来执行数值运算,如
awk
等。 - 例如:
result=$(awk 'BEGIN {print 5 + 3}') echo $result
这些方法可以根据需求和偏好进行选择。算术扩展是Shell脚本中最常见和直接的数值运算方法,而expr
和bc
命令则提供了更丰富的功能和更灵活的数学表达式。
九、变量的运用
1、变量命名规则
- 变量名可以包含字母、数字和下划线,但不能以数字开头。
- 推荐使用小写字母命名变量,以避免与系统环境变量冲突。
- 变量名区分大小写。
2、变量赋值和引用
- 变量的赋值使用等号
=
,赋值时变量名不需要加$
符号。 - 在引用变量时,使用
$
符号,例如$variable
。
3、单引号和双引号的影响
- 在双引号中引用变量会进行变量替换,而在单引号中不会。
- 例如:
name="John" echo "My name is $name" # 输出:My name is John echo 'My name is $name' # 输出:My name is $name
4、特殊变量
$0
:脚本名。$1
、$2
等:脚本的参数。$#
:参数的数量。$?
:上一个命令的退出状态。$@
:所有参数的列表。$*
:所有参数的列表,但是参数作为单个字符串。- 示例
-
$0:脚本名:
- 如果我们有一个名为
example.sh
的Shell脚本:#!/bin/bash echo "Script name: $0"
- 当我们执行这个脚本时:
bash example.sh
- 输出将会是:
Script name: example.sh
- 如果我们有一个名为
-
$1、$2 等:脚本的参数:
- 如果我们修改脚本为:
#!/bin/bash echo "First argument: $1" echo "Second argument: $2"
- 当我们执行脚本并传递参数时:
bash example.sh hello world
- 输出将会是:
First argument: hello Second argument: world
- 如果我们修改脚本为:
-
$#:参数的数量:
- 可以使用
$#
来获取传递给脚本的参数数量。 - 例如,在上一个示例中再添加一行:
#!/bin/bash echo "Number of arguments: $#"
- 当我们再次执行脚本时:
bash example.sh hello world
- 输出将会是:
Number of arguments: 2
- 可以使用
-
$?:上一个命令的退出状态:
$?
用于获取上一个命令的退出状态码。- 例如:
ls non-existent-file echo "Exit status: $?"
- 输出将会是:
ls: cannot access 'non-existent-file': No such file or directory Exit status: 2
-
$@ 和 $*:所有参数的列表:
$@
和$*
都用于获取所有传递给脚本的参数。- 区别在于,
"$@"
将每个参数作为一个独立的引号分隔的字符串,而"$*"
将所有参数作为一个单独的字符串。 - 例如:
#!/bin/bash echo "Using \$@:" for arg in "$@"; do echo "$arg" done echo "Using \$*:" for arg in "$*"; do echo "$arg" done
- 当我们执行脚本并传递参数时:
bash example.sh hello world
- 输出将会是:
Using $@: hello world Using $*: hello world
这些示例说明了特殊变量在Shell脚本中的使用方式以及它们的作用。
5、位置参数
- 位置参数是指向脚本传递的参数的变量,可以通过
$1
、$2
等来引用。 - 例如:
echo "First argument: $1" echo "Second argument: $2"
6、环境变量
- 环境变量是Shell进程启动时由操作系统设置的变量,可以通过
export
命令设置环境变量。 - 例如:
export PATH="/usr/local/bin:$PATH"
7、删除变量
- 使用
unset
命令可以删除一个变量,使其在Shell环境中不再存在。 - 例如:
unset name
通过合理运用这些变量相关的知识,可以使Shell脚本更加灵活、可配置,同时更好地处理输入参数、环境变量和脚本内部的数据处理。
十、只读变量
-
在Shell脚本中,可以使用
readonly
命令来声明只读变量,即变量的值在声明后不能被修改。只读变量通常用于存储常量或配置信息,以防止意外修改。 -
下面是使用
readonly
声明只读变量的示例:
#!/bin/bash
# 声明只读变量
readonly PI=3.14159
readonly GREETING="Hello, World"
# 尝试修改只读变量的值
PI=3.14 # 这个操作会导致错误
GREETING="Hi" # 这个操作也会导致错误
# 输出只读变量的值
echo "PI: $PI"
echo "Greeting: $GREETING"
当脚本尝试修改只读变量的值时,会收到类似如下的错误消息:
script.sh: line 6: PI: readonly variable
script.sh: line 7: GREETING: readonly variable
这表明脚本无法修改只读变量的值,从而确保了这些变量的不可变性。
十一、变量的替换与截取
在Shell脚本中,可以使用变量替换和截取来处理字符串。这些功能使得在脚本中对字符串进行操作更加灵活。下面是一些常见的变量替换和截取操作:
1、变量替换:
-
${变量名}
:使用变量的值。 -
${变量名:-默认值}
:如果变量未定义或为空,则使用默认值。 -
${变量名:=默认值}
:如果变量未定义或为空,则将其赋值为默认值。 -
${变量名:?错误消息}
:如果变量未定义或为空,则输出错误消息并退出脚本。 -
${变量名:+替换值}
:如果变量已定义且不为空,则使用替换值。
2、字符串截取:
-
${变量名:起始位置}
:从起始位置开始截取到字符串末尾。 -
${变量名:起始位置:长度}
:从起始位置开始截取指定长度的子串。 -
${变量名%匹配的后缀}
:从变量值的末尾开始,删除匹配的后缀部分。 -
${变量名#匹配的前缀}
:从变量值的开头开始,删除匹配的前缀部分。 -
${变量名%%匹配的后缀}
:从变量值的末尾开始,删除最长匹配的后缀部分。 -
${变量名##匹配的前缀}
:从变量值的开头开始,删除最长匹配的前缀部分。
下面是一些示例:
#!/bin/bash
# 定义变量
var="Hello, World!"
# 变量替换
echo "Variable value: ${var}" # 输出:Hello, World!
echo "Using default value: ${var:-Default}" # 输出:Hello, World!
echo "Setting default value: ${var:=Default}" # 输出:Hello, World!
echo "Display error message: ${var:?Error message}" # 输出:Hello, World!
echo "Replacing if defined: ${var:+Replaced}" # 输出:Replaced
# 字符串截取
echo "Substring from position 7: ${var:7}" # 输出:World!
echo "Substring from position 7 with length 5: ${var:7:5}" # 输出:World
echo "Remove suffix '!': ${var%*}" # 输出:Hello, World
echo "Remove prefix 'Hello, ': ${var#*, }" # 输出:World!
echo "Remove longest suffix '!': ${var%%!}" # 输出:Hello, World
echo "Remove longest prefix 'Hello, ': ${var##Hello, }" # 输出:World!
这些变量替换和截取操作使得在Shell脚本中对字符串进行操作变得更加灵活和方便。
出:Hello, World!
echo “Display error message: ${var:?Error message}” # 输出:Hello, World!
echo “Replacing if defined: ${var:+Replaced}” # 输出:Replaced
字符串截取
echo “Substring from position 7: ${var:7}” # 输出:World!
echo “Substring from position 7 with length 5: ${var:7:5}” # 输出:World
echo “Remove suffix ‘!’: ${var%}" # 输出:Hello, World
echo "Remove prefix 'Hello, ': ${var#, }” # 输出:World!
echo “Remove longest suffix ‘!’: ${var%%!}” # 输出:Hello, World
echo “Remove longest prefix 'Hello, ': ${var##Hello, }” # 输出:World!
这些变量替换和截取操作使得在Shell脚本中对字符串进行操作变得更加灵活和方便。