Shell
Shell 本身并不是内核的一部分,它只是站在内核的基础上编写的一个应用程序,是用户和Linux文件系统之间的桥梁。Shell 有自己的特殊性,就是开机立马启动,并呈现在用户面前;用户通过 Shell 来使用 Linux,不启动 Shell 的话,用户就没办法使用Linux。
1. shell的基础概念
Shell是一种解释性的命令行界面(CLI),它为用户提供了与操作系统内核进行交互的方式。在Linux和UNIX系统中,Shell是一种基本的用户界面工具,用于执行命令、管理文件系统、启动程序等。
常见的Shell有Bourne Shell(sh)、Bash(Bourne Again SHell)、C Shell(csh)和Korn Shell(ksh)等。其中,Bash是最常用的Shell,也是许多Linux发行版的默认Shell。
以下是Shell的一些主要特点和功能:
- 命令执行:Shell允许用户在命令行中输入命令,并将其传递给操作系统执行。用户可以执行各种任务,如文件操作、进程管理、软件安装等。
- 脚本编程:Shell还提供了脚本编程的功能,用户可以编写一系列的Shell命令并保存为脚本文件。这个脚本文件可以被执行,从而自动完成一系列的操作,提高效率和自动化任务。
- 变量和环境:Shell中可以定义和使用变量,用于存储数据和结果。用户可以通过变量来传递参数、存储临时数据等。此外,Shell还提供了环境变量,用于存储系统级别的配置信息,如路径、用户设置等。
- 通配符和通道:Shell支持通配符(如*、?)来匹配文件名和路径,从而方便进行文件操作。同时,Shell还支持管道(|)操作符,允许将一个命令的输出作为另一个命令的输入,实现数据流的传递和处理。
- 控制结构和循环:Shell提供了条件语句(如if-else)、循环语句(如for、while)等控制结构,使得用户可以根据条件执行不同的命令块或重复执行一系列的命令。
- 命令历史和自动补全:Shell会保存用户输入的命令历史记录,方便用户查找和重复执行之前执行过的命令。此外,Shell还支持自动补全,当用户输入命令或文件路径时,可以通过按下Tab键来自动完成。
Shell是Linux系统中强大且灵活的工具,它为用户提供了直接与操作系统交互的方式,可以根据用户需求执行各种任务和操作。通过学习和熟悉Shell,用户可以更好地管理和控制他们的系统。
1.1 Shell分类
Shell 是提供与内核沟通接口的命令解释器程序,但实际上 Shell 是这种解释器的统称,Linux 系统的 Shell 种类很多,包括 Bourne Shell(简称 sh)、Bourne Again Shell(简称 bash)、C Shell(简称 csh)、K shell(简称 ksh)等等。如下图:
也就是说 sh 和 bash 都是 Linux 系统 Shell 的一种,其中 bash 命令是 sh 命令的超集,大多数 sh 脚本都可以在 bash 下运行。Linux 系统中预设默认使用的就是 bash。
1.2 She Bang
She Bang是Shell脚本的第一行:#!/bin/bash
,通常是用来指定要运行的脚本的解释器。
#!
就是告诉系统解释此脚本文件的 Shell 程序在哪(其后路径所指定的程序)/bin/bash
是解释器的路径。
所以 She Bang的格式重要,格式不正确会导致命令工作不正常。其中大家要记住的是:
- She Bang应在脚本的第一行。
- 在 #! 和解释器的路径之间, # 之前不应有任何空格。
1.3 运行脚本的方式
sh
和bash
- 开启一个新进程运行脚本
- 变量不能共享
source
和.
- 在当前进程中运行脚本(所以环境变量要使用source,不然新进程中访问不到)
- 变量可以共享
.
代表带路径运行
- 带路径运行
- 运行的脚本必须有执行权限
1.4 脚本
在计算机编程中,脚本是用于适当的运行时环境的一组命令,这些命令用于自动执行任务。
我们经常说的 Shell 脚本,其实就是利用 Shell 的功能,编写能够直接运行的脚本文件。
2. Shell 基础
2.1 注释
: '
这就是注释
'
<< EOF
用分界符注释
不是EOF也可以,但是EOF是规范
EOF
2.2 变量
在Shell中,变量是用来存储数据的一种方式。它们可以是数字、字符串或任何其他类型的数据。
Shell中的变量名是以美元符号($)开头的字符串,其后跟着变量名。不能在变量名中使用空格或标点符号(除了下划线),也不能以数字开头。变量名区分大小写。
下面是一些常见的Shell变量:
- 环境变量:这些变量包含了有关系统和用户环境的信息。例如,PATH变量定义了Shell在哪些目录中查找可执行文件。
- 用户定义变量:这些变量由用户自己定义,用于存储特定的数据。例如,可以定义一个变量来存储一个目录的路径名。
- 位置参数变量:这些变量用于存储命令行参数。例如,$0表示命令本身的名称,$1、$2等表示命令行中的第一个、第二个参数等。
- 预定义变量:这些变量由Shell预先定义,用于存储有关Shell状态和配置的信息。例如,$HOME变量存储当前用户的主目录路径。
Shell中使用等号(=)来给变量赋值,例如:
MY_VAR="Hello, world!"
在Shell中,可以使用echo命令来显示变量的值,例如:
echo $MY_VAR
输出结果为:
Hello, world!
可以使用unset命令来删除一个变量,例如:
unset MY_VAR
以上就是Shell中的变量的基本知识。掌握了变量的使用方法,可以更加灵活地编写Shell脚本。
2.3 引号
在Shell中,引号用于将字符串括起来。引号可以是单引号、双引号或反引号。
- 单引号:单引号内的所有字符都被视为普通字符,也可以原样输出,包括变量和命令替换。例如:
echo 'Hello $USER'
输出结果为:
Hello $USER
- 双引号:双引号内的变量和命令将会被展开。例如:
echo "Hello $USER"
输出结果为:
Hello username
- 反引号:反引号用于执行命令并将其输出作为字符串。例如:
echo "The date is `date`"
输出结果为:
The date is Wed Nov 24 04:27:38 UTC 2021
需要注意的是,在使用双引号时,如果想要将某些特殊字符视为普通字符而不进行转义,可以使用反斜杠(\)对其进行转义。例如:
echo "Hello \$USER"
输出结果为:
Hello $USER
在一些情况下,引号的使用可以影响变量的展开和命令的执行结果。因此,在编写Shell脚本时,需要根据具体情况来选择合适的引号。
2.4 位置参数
Shell中的位置参数指的是命令行上给定的参数,它们按照出现的顺序被依次编号为$1、$2等。其中,$0表示命令本身的名称。
例如,假设有一个名为test.sh的脚本,可以这样在命令行上给其传递参数:
./test.sh arg1 arg2 arg3
在test.sh脚本中,可以使用位置参数来获取这些参数值:
echo "第一个参数为:$1"
echo "第二个参数为:$2"
echo "第三个参数为:$3"
输出结果为:
第一个参数为:arg1
第二个参数为:arg2
第三个参数为:arg3
如果需要获取所有的位置参数,可以使用 @ 或 @或 @或*来表示。这两个变量都表示所有的位置参数,不同之处在于如果将它们放在引号内, ∗ 会将所有参数解释成一个字符串,而 *会将所有参数解释成一个字符串,而 ∗会将所有参数解释成一个字符串,而@则会将每个参数解释成一个独立的字符串。
例如:
#!/bin/bash
echo "所有的参数为:$*"
echo "所有的参数为:$@"
执行命令:
./test.sh arg1 arg2 arg3
输出结果为:
所有的参数为:arg1 arg2 arg3
所有的参数为:arg1 arg2 arg3
需要注意的是,如果参数中包含空格等特殊字符,需要用引号将其括起来以避免解释错误。例如:
./test.sh "arg1 with space" arg2 "arg3 with space"
在脚本中使用位置参数时,也可以通过shift命令将参数左移一位,以便获取下一个参数。例如:
#!/bin/bash
echo "第一个参数为:$1"
shift
echo "第二个参数为:$1"
执行命令:
./test.sh arg1 arg2 arg3
输出结果为:
第一个参数为:arg1
第二个参数为:arg2
这就是Shell中的位置参数。通过它们,可以让Shell脚本处理多个参数,并根据具体需求对其进行处理。
2.5 特殊变量
Shell中有一些特殊变量,它们具有特定的含义和功能。以下是一些常见的Shell特殊变量:
$0
这个变量保存了当前脚本或命令的名称。例如,在一个名为myscript.sh
的Shell脚本中,$0
将会是myscript.sh
。
#!/bin/bash
echo "The name of this script is: $0"
执行上述脚本,输出结果为:
The name of this script is: myscript.sh
$1
,$2
, …
这些变量用来表示位置参数,即命令行上给定的参数。例如,在执行脚本时,可以使用$1
来获取第一个参数,使用$2
来获取第二个参数,以此类推。
#!/bin/bash
echo "The first argument is: $1"
echo "The second argument is: $2"
执行上述脚本并传递两个参数,输出结果为:
The first argument is: arg1
The second argument is: arg2
$@
这个变量表示所有的位置参数。它将所有传递给脚本或命令的参数看作一个整体,可以使用循环来遍历所有参数。
#!/bin/bash
echo "All arguments are: $@"
执行上述脚本并传递三个参数,输出结果为:
All arguments are: arg1 arg2 arg3
可以将$@
用于循环中,以便遍历所有位置参数:
#!/bin/bash
for arg in "$@"
do
echo "Argument: $arg"
done
执行上述脚本并传递三个参数,输出结果为:
Argument: arg1
Argument: arg2
Argument: arg3
$#
这个变量表示位置参数的个数,不包括脚本或命令本身的名称。
#!/bin/bash
echo "The number of arguments is: $#"
执行上述脚本并传递三个参数,输出结果为:
The number of arguments is: 3
$?
这个变量表示上一个命令的退出状态码。如果命令执行成功,则其值为0;如果命令执行失败,则其值为非零。
#!/bin/bash
ls /notexist
echo "The exit status of the previous command is: $?"
执行上述脚本,由于ls /notexist
命令找不到指定目录,其退出状态码为1,因此输出结果为:
ls: cannot access '/notexist': No such file or directory
The exit status of the previous command is: 1
$$
这个变量表示当前脚本或命令的进程ID(PID)。它在脚本中可以用来标识当前进程的唯一性。
#!/bin/bash
echo "The PID of this script is: $$"
执行上述脚本,输出结果为:
The PID of this script is: 1234
$!
这个变量表示最后一个在后台运行的命令的进程ID。可以用来获取最后一个后台任务的PID。
#!/bin/bash
sleep 5 &
echo "The PID of the last background command is: $!"
执行上述脚本,由于sleep 5 &
命令被放到后台运行,其PID为1234,因此输出结果为:
The PID of the last background command is: 1234
$*
这个变量表示所有的位置参数作为单个字符串。它将所有参数解释成一个字符串,而不是一个数组。可以使用引号将其括起来,以保留参数之间的空格。
#!/bin/bash
echo "All arguments as a single string: $*"
执行上述脚本并传递三个参数,输出结果为:
All arguments as a single string: arg1 arg2 arg3
$IFS
这个变量表示内部字段分隔符(Internal Field Separator)。它定义了Shell将输入拆分成字段(或单词)的分隔符,默认情况下,它包含空格、制表符和换行符。
#!/bin/bash
echo "The default value of IFS is: '$IFS'"
IFS=":"
echo "After changing IFS to ':', it is: '$IFS'"
执行上述脚本,输出结果为:
The default value of IFS is: '
'
After changing IFS to ':', it is: ':'
$HOME
这个变量表示当前用户的主目录。
#!/bin/bash
echo "The home directory of the current user is: $HOME"
执行上述脚本,输出结果为:
The home directory of the current user is: /home/user
$PWD
这个变量表示当前工作目录的路径。
#!/bin/bash
echo "The current working directory is: $PWD"
执行上述脚本,输出结果为:
The current working directory is: /home/user
这些特殊变量可以在Shell脚本中使用,让你可以获取和处理各种信息,以及控制脚本的行为。通过利用这些变量,你可以编写更灵活、可配置的Shell脚本。
2.6 字符串
在Shell编程中,字符串是一个由字符组成的序列。Shell提供了多种方式来处理字符串,包括字符串拼接、提取子串、替换等操作。
-
字符串的定义
-
单引号:使用单引号
'
可以定义一个单行字符串,其中的内容会被原样输出,不会进行变量替换和转义字符的解释。str='Hello, World!'
-
双引号:使用双引号
"
可以定义一个单行字符串,其中的内容可以包含变量、转义字符和命令替换。name='Alice' str="Hello, $name!"
-
Here文档:使用Here文档可以定义包含多行内容的字符串,可以使用任意的定界符。
str=$(cat <<EOF Hello, World! EOF )
-
-
字符串拼接
使用连接操作符.
可以将两个字符串拼接在一起。str1='Hello,' str2=' World!' result=$str1$str2 echo $result # 输出:Hello, World!
-
字符串长度
使用${#string}
可以获取字符串的长度。str='Hello, World!' len=${#str} echo $len # 输出:13
-
子串提取
-
从左边开始提取指定长度的子串:
${string:start:length}
str='Hello, World!' sub=${str:0:5} echo $sub # 输出:Hello
-
从右边开始提取指定长度的子串:
${string:start-length}
str='Hello, World!' sub=${str:7-5:5} echo $sub # 输出:World
-
-
字符串替换
-
替换第一个匹配的子串:
${string/substring/replacement}
str='Hello, World!' new_str=${str/World/China} echo $new_str # 输出:Hello, China!
-
替换所有匹配的子串:
${string//substring/replacement}
str='Hello, World!' new_str=${str//o/O} echo $new_str # 输出:HellO, WOrld!
-
-
字符串切割为数组
使用IFS(内部字段分隔符)将字符串切割为数组。str='apple,banana,cherry' IFS=',' read -ra arr <<< "$str" echo ${arr[1]} # 输出:banana
-
字符串比较
使用=
、!=
、>
、<
等符号进行字符串比较。str1='apple' str2='banana' if [[ $str1 == $str2 ]]; then echo 'Equal' else echo 'Not equal' # 输出:Not equal fi
-
在Shell中,
#*
和%*
是用于字符串处理的特殊符号,用于匹配和删除字符串中的特定模式。-
#*
:-
语法:
${variable#*pattern}
-
功能:从变量
${variable}
的开头开始,删除第一个匹配pattern
的字符串及其左边的内容,返回剩余的部分。 -
示例:
str="Hello, World!" echo ${str#*o} # 输出:llo, World!
在这个示例中,
${str#*o}
会删除str
变量中第一个匹配字母o
及其左边的所有字符,返回剩余的部分llo, World!
。 -
-
%*
:-
语法:
${variable%pattern*}
-
功能:从变量
${variable}
的结尾开始,删除最后一个匹配pattern
的字符串及其右边的内容,返回剩余的部分。 -
示例:
str="Hello, World!" echo ${str%o*} # 输出:Hello, W
在这个示例中,
${str%o*}
会删除str
变量中最后一个匹配字母o
及其右边的所有字符,返回剩余的部分Hello, W
。 -
-
这些是Shell中处理字符串的一些常用操作和技巧。通过灵活运用这些操作,可以对字符串进行各种处理和转换,从而实现复杂的逻辑和功能。
2.7 数组
在Shell脚本中,数组是一种特殊的变量类型,用于存储一组相关的值。Shell支持一维数组,即只能存储一列值。
-
声明数组:
-
语法:
array_name=(value1 value2 ... valuen)
-
示例:
fruits=("apple" "banana" "orange")
在这个示例中,我们声明了一个名为
fruits
的数组,并初始化了三个元素"apple"
、"banana"
和"orange"
。 -
-
读取数组元素:
-
语法:
${array_name[index]}
-
示例:
echo ${fruits[0]} # 输出:apple
数组元素可以通过索引(从0开始)来访问。
${fruits[0]}
表示fruits
数组中的第一个元素。 -
-
修改数组元素:
-
语法:
array_name[index]=new_value
-
示例:
fruits[1]="grape" echo ${fruits[1]} # 输出:grape
fruits[1]="grape"
将fruits
数组中索引为1的元素修改为"grape"
。 -
-
获取数组长度:
-
语法:
${#array_name[@]}
-
示例:
length=${#fruits[@]} echo $length # 输出:3
${#fruits[@]}
返回fruits
数组的长度,即元素的个数。 -
-
遍历数组:
-
示例:
for fruit in ${fruits[@]}; do echo $fruit done
这个示例中,通过
for
循环遍历fruits
数组的所有元素,并逐个输出。 -
-
添加新元素到数组:
-
示例:
shell复制代码fruits+=("pear") echo ${fruits[@]} # 输出:apple banana orange grape pear
通过使用
+=
操作符,可以向数组末尾添加新的元素。 -
-
删除数组元素:
-
示例:
shell复制代码unset fruits[1] echo ${fruits[@]} # 输出:apple orange grape
-
使用unset
命令可以删除数组中指定索引位置的元素。
-
切片数组:
-
示例:
shell复制代码echo ${fruits[@]:1:2} # 输出:orange grape
-
${fruits[@]:1:2}
表示从索引为1的位置开始,取2个元素,形成切片数组。
数组在Shell脚本中用于存储多个相关的值,可以方便地进行数据的组织和处理。通过索引访问、修改元素,获取数组长度以及遍历数组等操作,可以灵活地操作和处理数组中的数据。