bash shell
第一部分:基础
1. Bash 简介
1.1 Shell 是什么
Shell 是计算机操作系统中的一种用户界面,它为用户提供了与操作系统内核进行交互的方式。通过 Shell,用户可以执行命令、运行程序、管理文件系统等。Shell 是一个解释性的编程语言,它接受用户输入的命令,并将其转换为操作系统可以理解的指令。
1.2 为什么使用 Shell?
- 命令行的强大性: Shell 提供了强大的命令行接口,使得用户可以通过简短的命令完成复杂的任务。
- 脚本编程: 使用 Shell 脚本,用户可以编写一系列的命令,形成可重复执行的脚本,实现自动化和批量处理。
- 系统管理: 系统管理员和开发人员常常使用 Shell 来管理和配置操作系统,执行系统维护任务。
- 灵活性和定制性: Shell 具有高度的灵活性,用户可以根据需要定制环境,创建别名,以及编写自定义函数。
1.3 Bash 是什么
- Bourne Shell: Bash(Bourne Again SHell)是 Bourne Shell 的一个扩展。Bourne Shell 是由 Stephen Bourne 开发的原始 Unix Shell。
- GNU 项目: Bash 是 GNU 项目的一部分,在 1989 年由 Brian Fox 开发,目的是作为自由软件基金会 GNU 工具集的一部分。
- 标准 Shell: Bash 是许多 Linux 发行版和 macOS 默认的标准 Shell(macos从13开始默认转向zsh),也是许多脚本和系统管理任务的首选工具。
2. Bash 的基本语法
2.1 命令行结构
Bash命令行的基本结构包括命令、选项、参数和控制操作符。
1 | command [options] [arguments] |
- 命令(command(命令)): 要执行的实际命令或程序的名称。例如,
ls
、cp
、echo
等都是命令。 - 选项(options): 有时也称为标志或开关,用于修改命令的行为。选项通常以单个破折号(
-
)或双破折号(--
)开始。例如,ls -l
中的-l
是一个选项。 - 参数(arguments): 传递给命令的输入数据或操作对象。参数是命令的具体操作目标。例如,
cp source destination
中的source
和destination
就是参数。
2.2 变量
在Bash中,变量是用来存储数据值的符号名称。不同于许多其他编程语言,Bash 并不区分变量的类型。本质上说,Bash 变量是字符串,但在某些情况下,Bash 允许对变量进行算术运算和比较。
变量可以存储各种类型的数据,包括字符串、数字和数组等。变量的命名遵循一些规则:
- 变量名是大小写敏感的。
- 变量名可以包含字母、数字和下划线,但不能以数字开头。
- 通常,使用大写字母表示环境变量,而使用小写字母表示用户定义的变量
2.2.1 变量赋值
- 简单赋值
1 | variable_name=value #等号两边不能有空格,如果有空格则认为空格后面是命令的参数,执行错误 |
- 使用命令输出赋值 (命令替换)
1 | var=$(command) #或 |
- 环境变量赋值
可以使用export
命令将变量声明为环境变量,使其在脚本中和子进程中可用。
1 | export MY_VARIABLE="some_value" |
- 直接读取用户输入赋值
使用read
命令可以直接从用户输入中获取值并赋给变量。
1 | ➜ ~ echo "Enter your name:" |
read 也可以同时给多个变量赋值,多个变量之间用空格分隔
1 | ➜ ~ read name age |
- 数组赋值
1 | my_array=("value1" "value2" "value3") |
- 默认值和替代值
可以使用${undefined_var:-default_value}
来为变量设置默认值,如果变量未设置,则使用默认值。
1 | export LOG_DIR="${LOG_DIR:-/var/log}" # 假设有一个环境变量LOG_DIR表示日志目录,可以这样设置默认值 |
- 一次为多个变量赋值
可采用read 和here stings方式或<方式为多个变量赋值
1 | ➜ ~ read name age <<< "zs 37" |
- 间接赋值
使用间接引用可以通过变量的值构建变量名,这对于动态引用变量很有用。
1 | bash-3.2$ var_name=age |
2.2.2 引用变量
- 通过使用”$”符号来实现变量引用
1 | ➜ ~ name="beizi" |
- 使用花括号括起变量,可明确变量的边界
1 | ➜ ~ echo "${name}_age is $age" |
双引号中的变量会进行变量替换,而单引号则不会,会保留字符串的字面量。
使用${!var}间接引用变量,将使用var的值作为变量名引用。
- 命令替换
使用$(command)
或反引号``可以将命令的输出赋值给变量。
- 位置参数
在脚本中,可以通过$1
、$2
、$3
等位置参数引用脚本的参数
2.2.3 只读变量
可以使用readonly
命令使变量变为只读,一旦变为只读,其值将不能被修改。
1 | readonly hostname |
2.2.4 删除变量
使用unset
命令可以删除一个变量。
1 | unset hostname |
2.2.5 特殊变量
Bash Shell中有一些特殊的变量,它们具有特殊的含义。例如:
$0
:脚本名称$1
,$2
, …:脚本参数$#
:脚本参数的个数$@
: 传递给脚本或函数的所有参数的列表。$*
: 传递给脚本或函数的所有参数作为单个字符串,不保留参数之间的空格。$?
:上一个命令的退出状态,执行成功$?的值为0,否则为非零值。$!
:后台运行的最后一个作业的进程ID。$IFS
: 内部字段分隔符,用于指定字段之间的分隔符,默认为包含空格、制表符和换行符的字符串。
2.2.6 环境变量
环境变量是操作系统或特定程序所使用的动态值,其值可以在操作系统级别或在特定进程中设置。这些变量对于控制系统行为、提供配置选项以及与不同程序之间共享信息非常重要。以下是环境变量的一些详细介绍:
作用:
- 环境变量提供了一种在操作系统和应用程序之间共享配置信息的标准方式。
- 它们允许您更改系统的行为,例如更改默认编辑器、设置语言首选项等。
设置环境变量:
- 在 Unix/Linux 系统中,可以通过在 shell 中使用
export
命令来设置环境变量。例如:export PATH=/usr/local/bin:$PATH
。 - 在 Windows 中,可以通过控制面板或命令行使用
set
命令设置环境变量。例如:set PATH=C:\Program Files\MyApp;%PATH%
。
- 在 Unix/Linux 系统中,可以通过在 shell 中使用
常见环境变量:
在 Linux 系统中,有许多常见的环境变量,它们用于控制系统行为、配置应用程序和提供系统信息。以下是一些常见的 Linux 环境变量:
PATH:用于指定系统在哪些目录中查找可执行文件。这是一个由冒号分隔的目录列表。
HOME:指定当前用户的主目录路径。
USER:指定当前登录的用户名。
LANG:指定系统默认的语言环境,影响字符编码和语言设置。
PWD:指定当前工作目录的路径。
SHELL:指定当前用户的默认 shell 的路径。
TERM:指定当前终端的类型,影响终端的显示行为。
PS1:指定 shell 提示符的格式。
LD_LIBRARY_PATH:用于指定动态链接器在哪些目录中查找共享库文件。
DISPLAY:指定 X 窗口系统的显示服务器。
EDITOR:指定系统默认的文本编辑器。
TMPDIR:指定临时文件目录的路径。
TZ:指定系统的时区。
**LC_***:一系列环境变量,用于指定系统的本地化设置,如
LC_COLLATE
、LC_NUMERIC
、LC_TIME
等。
这些环境变量在 Linux 系统中广泛使用,对于系统配置、用户交互和程序运行都有重要作用。可以使用
env
命令来查看当前 shell 中的所有环境变量,或者使用echo $VARIABLE_NAME
来查看特定环境变量的值。环境变量的继承:
- 当一个进程创建子进程时,子进程会继承父进程的环境变量。
- 当您在 shell 中设置环境变量时,只有当前 shell 及其子 shell 可以访问该变量。
用途:
- 控制系统行为:例如,
LANG
环境变量用于设置系统的语言环境。 - 配置应用程序:例如,
PATH
环境变量用于指定系统中的可执行文件目录。 - 共享信息:例如,某些应用程序可能会使用特定环境变量来确定配置文件的位置或其他重要信息。
- 控制系统行为:例如,
查看环境变量:
- 在 Unix/Linux 中,可以使用
env
命令查看当前 shell 中的所有环境变量。 - 在 Windows 中,可以使用
set
命令查看当前 shell 中的所有环境变量。
- 在 Unix/Linux 中,可以使用
总的来说,环境变量在操作系统和应用程序中起着重要作用,它们提供了一种在系统中配置和共享信息的灵活方式。
2.2.7 变量的作用域
在bash中,变量的作用域有两种:全局作用域和局部作用域。
- 在脚本最外层定义的变量具有全局作用域,任何地方都能访问这些全局变量,包括在函数内部。这些变量在整个脚本中都是可见的。
1 |
|
- 在函数内使用 local 定义的变量具有局部作用域,只能在函数内访问,无法在函数外部直接访问。函数执行完时,局部变量的生命周期也结束了。
1 |
|
需要注意,如果在函数内部不使用 local
关键字声明变量,那么该变量将具有全局作用域,即使它是在函数内部定义的。使用 local
关键字可以确保变量在函数内部是局部的,而在函数外部是不可见的。
变量的作用域对于避免命名冲突、保持代码清晰和维护性非常重要。确保在需要的情况下使用局部变量,以避免不必要的全局变量。
2.3 数组
在Bash中,数组是一种有序集合,可以存储多个元素,并通过索引来访问这些元素。Bash支持一维数组,数组的元素可以包含字符串或数字。
2.3.1 声明数组
使用以下语法可声明数组:
1 | arr=("element1" "element2" "element3") |
2.3.2访问数组元素
通过索引可以访问数组元素,索引从0开始
1 | echo ${arr[0]} # 输出第一个元素 |
2.3.3 数组长度:
获取数组的长度可以使用 ${#array[@]}
:
1 | echo "arr length: ${#arr[@]}" |
2.3.4 追加元素:
可以使用括号和+=操作符来追加元素到数组:
1 | arr+=("new_element") |
2.3.5 删除数组
2.3.6 遍历数组
- 可以使用循环来遍历数组中的所有元素:
1 | for i in "${arr[@]}"; do |
- 使用
!
间接引用遍历数组
1 | animals=("dog" "cat" "pig" "horse") |
- 还可以使用 C 风格的
for ((i = 0; i < ${#array[@]}; i++)); do ... done
循环来遍历数组元素:
1 | animals=("dog" "cat" "pig" "horse") |
- 使用
while
循环遍历数组:
可以使用 while
循环来遍历数组:
1 | animals=("dog" "cat" "pig" "horse") |
可以使用while
和shift
命令遍历并删除数组中的元素:
1 | animals=("dog" "cat" "pig" "horse") |
2.3.7 关联数组
Bash 4.0及以上版本支持关联数组,可以使用字符串作为索引:
1 | # 使用declare -A 声明关联数组 |
2.4 字符串操作
在Bash中,有很多用于字符串操作的内建功能。参数扩展操作符是一种用于对变量值进行处理的机制,可以在变量替换时使用。以下是常用的参数扩展操作符:
在 Bash 中,参数扩展操作符是一种用于对变量值进行处理的机制,可以在变量替换时使用。以下是常用的参数扩展操作符:
- **
${parameter}
**:变量的值。 - **
${parameter:-word}
**:如果变量parameter
已设置且非空,则返回其值,否则返回word
。 - **
${parameter:=word}
**:如果变量parameter
已设置且非空,则返回其值,否则将parameter
设置为word
,并返回word
。 - **
${parameter:+word}
**:如果变量parameter
已设置且非空,则返回word
,否则返回空字符串。 - **
${parameter:offset:length}
**:从变量parameter
的值中提取子字符串,从offset
位置开始,长度为length
。 ${!prefix*}
或 **${!prefix@}
**:匹配以prefix
开头的变量名,返回这些变量名组成的列表。- **
${#parameter}
**:返回变量parameter
的长度。 ${parameter#word}
和 **${parameter##word}
**:从变量parameter
的值中删除匹配word
的最短或最长前缀。${parameter%word}
和 **${parameter%%word}
**:从变量parameter
的值中删除匹配word
的最短或最长后缀。${parameter/pattern/string}
和 **${parameter//pattern/string}
**:将变量parameter
的值中匹配pattern
的部分替换为string
,只替换一次或全部替换。
这些参数扩展操作符提供了丰富的功能,可用于对变量值进行各种处理和操作。使用这些操作符可以方便地进行字符串处理、条件判断等操作。
${parameter^^}
和 **${parameter,,}
**:将变量parameter
的值转换为大写或小写。**
${parameter@operator}
**:执行数组操作符,如Q
表示将数组转换为引用列表,A
表示将数组转换为分配列表,E
表示对数组进行扩展,P
表示对数组进行排序。**
${parameter:?[error_message]}
**:如果变量parameter
未设置或为空,则输出错误信息并退出。**
${parameter:?}
**:如果变量parameter
未设置或为空,则输出标准错误信息并退出。${parameter^}
和 **${parameter,}
**:将变量parameter
的值中的首字符转换为大写或小写。${parameter^pattern}
和 **${parameter,pattern}
**:将变量parameter
的值中匹配pattern
的部分的首字符转换为大写或小写。${parameter^^pattern}
和 **${parameter,,pattern}
**:将变量parameter
的值中匹配pattern
的部分全部转换为大写或小写。**
${parameter@Q}
**:将变量parameter
的值转换为引用列表(单词间用引号分隔)。**
${parameter@A}
**:将变量parameter
的值转换为分配列表(单词间用空格分隔)。**
${parameter@E}
**:对数组parameter
进行扩展,返回分配列表。**
${parameter@P}
**:对数组parameter
进行排序。${parameter#*}
和 **${parameter##*}
**:从变量parameter
的值中删除第一个或最后一个*
之前的内容。${parameter%*}
和 **${parameter%%*}
**:从变量parameter
的值中删除第一个或最后一个*
之后的内容。**
${parameter/pattern}
**:删除变量parameter
的值中第一个匹配pattern
的部分。**
${parameter//pattern}
**:删除变量parameter
的值中所有匹配pattern
的部分。**
${parameter/#pattern}
**:如果变量parameter
的值以pattern
开头,则删除匹配部分。**
${parameter/%pattern}
**:如果变量parameter
的值以pattern
结尾,则删除匹配部分。
2.4.1 常用基本操作
- 获取字符串长度:
${#string}
- 子字符串提取:
${string:start:length}
- 删除子字符:
${variable#pattern}
:从变量的开头删除最短匹配 pattern
的子字符串。${variable##pattern}
:从变量的开头删除最长匹配 pattern
的子字符串。${variable%pattern}
:从变量的结尾删除最短匹配 pattern
的子字符串。${variable%%pattern}
:从变量的结尾删除最长匹配 pattern
的子字符串。
查找子字符串:
[[ $string == *"substring"* ]]
测试是否包含子字符串替换子字符串:
${variable/pattern}
:不会返回匹配的部分,而是将匹配的部分替换为空字符串,并返回结果。这是一种替换操作,而不是查找操作替换第一个匹配的字符
${string/pattern/replacement}
替换所有匹配的字符
${string//pattern/replacement}
转换大小写:
${string,,}
,${string^^}
2.4.2 字符串比较和判定
- 判断空值:
[ -z "$string" ]
- 判断非空值
[ -n "$string" ]
- 字符串比较:
[[ $string1 == $string2 ]]
- 字符串长度比较:
[ "${#string1}" -eq "${#string2}" ]
2.5 数值运算
在 Bash 中,进行整数运算有几种方式,其中最常用的方式是使用双括号 ((...))
或 expr
命令。
- 双括号
((...))
是 Bash 中进行整数运算的一种常见方式。它支持各种算术运算符,如加法、减法、乘法、除法等。
1 | num1=10 |
expr
命令用于在 Bash 中进行基本的整数运算。它采用与 C 语言相似的语法。
1 | num1=10 |
let
命令是用于在 Bash 中进行整数运算的另一种方法。
1 | num1=10 |
在早期版本的 Bash 中,支持使用 $[...]
进行整数运算,例如:
1 | result=$[10 + 5] |
这种语法是一种旧式的整数运算语法,在较新版本的 Bash 中已经不再推荐使用,因为它已经被双括号 ((...))
语法取代。使用 $[...]
的方式在某些情况下可能会导致不同版本的 Bash 兼容性问题。
推荐使用更现代的双括号 ((...))
语法进行整数运算,因为它更加直观和易读,并且不会产生兼容性问题。
注意事项
- 这些方法都仅支持整数运算,不支持浮点数运算。
- 在进行算术运算时,变量名前面不需要加上
$
符号。 - 如果要在脚本中进行大量的数学运算,
((...))
通常是最常用的方法,因为它更直观、易读,并且不需要额外的命令。 - 使用
expr
命令时需要注意,表达式中的运算符和操作数之间需要用空格隔开,而且特殊字符(如*
)需要转义或用引号括起来,否则可能会导致意外的结果。
以上所有的数值运算都是在整数范围内进行的。如果你需要进行浮点数运算,可以使用 bc
命令,它是一个用于数学计算的命令行工具,其名称来源于 “Basic Calculator”。例如:
1 | result=$(echo "scale=2; 10 / 3" | bc) |
1 | calculate() { |
1 | bc |
1 | echo "5 + 3" | bc |
当 bc
遇到错误时,会输出错误消息并退出。你可以通过捕获 stderr
来处理错误。
bc中一些常用的数学函数
三角函数
s()
:正弦函数。c()
:余弦函数。a()
:反正弦函数。l()
:反余弦函数。e()
:正切函数。j()
:反正切函数。
对数函数
l()
:自然对数函数(以 e 为底)。e()
:以 10 为底的对数函数。
指数函数
e()
:自然指数函数。^
:幂函数。
其他
sqrt()
:平方根函数。scale
:设置小数点后的精度。
2.6 注释和文档
在Shell脚本中,您可以使用注释和文档来提高代码的可读性和可维护性。
2.6.1 注释
在Shell脚本中,注释以#
字符开始,并且一直延伸到行的末尾。注释用于解释代码的目的、提供额外的说明和指导。注释不会被解释器执行,因此它们对脚本的运行没有影响。
1 | # 这是一个注释,用于解释下面的命令的目的 |
2.6.2 文档
文档通常在脚本的顶部提供,用于描述脚本的用途、作者、版本等信息。文档通常包含在多行注释块中,并且使用特殊的标记(如 @
或 #
)来表示不同类型的信息。
1 |
|
文档部分可以包含脚本的名称、作者、版本号、日期、描述等信息。这些信息对于其他人阅读和理解脚本非常有帮助。可以根据需求扩展文档部分,例如添加参数说明、示例用法等。
通过使用注释和文档,您可以使Shell脚本更易于阅读、理解和维护。这有助于促进代码的可维护性和可重用性。
3. 控制流结构
3.1 条件语句
在Shell脚本中,条件语句允许您根据条件的真假来执行不同的代码块。
bash条件测试
3.1.1 if
语句:
if
语句用于在条件为真时执行代码块。它的基本结构如下:
1 | if [ condition ]; then |
其中,condition
是一个表达式,它可以是命令的退出状态、比较操作或其他测试。条件可以包含逻辑运算符(如 &&
、||
)和括号来组合多个条件。
1 | # 示例:检查文件是否存在 |
3.1.2 case
语句:
case
语句用于测试一个变量与多个模式之间的匹配情况。它的基本结构如下:
1 | case expression in |
expression
是要测试的变量,pattern
是匹配模式。如果expression
匹配了某个模式,那么与该模式相关联的代码块将被执行。
1 | # 示例:根据用户输入执行不同的操作 |
3.1.3 test
命令:
test
命令用于执行各种条件测试,并返回相应的退出状态。它经常与 if
语句结合使用,以便进行文件测试、字符串比较、数值比较等。
1 | # 示例:检查变量是否为空 |
这些条件语句可以帮助您根据不同的条件执行不同的代码,从而实现更灵活的脚本逻辑。
在 Bash 中,[ ]
和 [[ ]]
都是条件测试命令。它们之间有一些区别:
语法:
[ ]
是传统的条件测试命令,它使用单词形式,并且需要在每个条件测试之间使用空格来分隔。[[ ]]
是 Bash 的扩展条件语法,它使用双方括号,并且更加灵活,不需要像[ ]
那样严格使用空格来分隔条件。
功能:
[ ]
提供基本的条件测试功能,例如测试变量是否为空、字符串比较、文件属性等。[[ ]]
提供了更多的功能和灵活性,例如支持模式匹配、正则表达式、逻辑运算符等,并且更加安全,不会出现某些意外行为。
性能:
[[ ]]
通常比[ ]
更快,因为它是 Bash 的内置命令,而不是外部命令。此外,[[ ]]
的语法更简洁,不需要像[ ]
那样频繁地调用外部命令。
综上所述,[[ ]]
是 Bash 推荐的条件测试语法,它提供了更多的功能和更好的性能,并且更加安全。在编写 Bash 脚本时,建议尽可能使用 [[ ]]
来进行条件测试。
3.2 循环结构
在Shell脚本中,循环结构允许您多次执行一段代码,直到满足某个条件。Shell脚本支持 for
、while
和 until
循环。
3.2.1 for
循环:
for
循环用于迭代一个列表中的元素,并对每个元素执行一段代码。其基本结构如下:
1 | for variable in list; do |
其中,variable
是用于保存当前迭代元素的变量,list
是一个包含多个元素的列表。在每次迭代中,variable
将依次取列表中的每个元素的值。
1 | # 示例:遍历数组中的元素 |
3.2.2 while
循环:
while
循环用于在条件为真时重复执行一段代码。其基本结构如下:
1 | while condition; do |
在每次迭代中,condition
条件表达式被评估,如果为真,则执行循环体中的代码块。
1 | # 示例:计算1到10的和 |
3.2.3 until
循环:
until
循环与 while
循环相反,它在条件为假时执行一段代码。其基本结构如下:
1 | until condition; do |
与 while
循环不同,until
循环在条件为假时执行循环体中的代码块,直到条件为真。
1 | # 示例:生成随机数直到大于等于 90 |
这些循环结构允许您编写更灵活的脚本,以便根据条件重复执行代码块或遍历列表中的元素。
3.2.4 select 循环
select
是 Bash shell 中的一个内置命令,用于创建交互式菜单。它可以帮助用户从一组预定义的选项中进行选择,并根据用户的选择执行相应的操作。
select
命令的基本语法如下:
1 | select variable in option1 option2 option3 ... |
在这个语法中:
variable
是一个变量名,用于存储用户选择的选项。option1 option2 option3 ...
是一系列可选的选项,它们会显示给用户。do
和done
之间是一个代码块,其中包含根据用户选择执行的操作。
select
命令会显示一个菜单,列出由 in
后面的选项定义的选项,并等待用户输入选择。一旦用户选择了一个选项,脚本会将选项的编号或者内容存储到指定的变量中,并执行 do
和 done
之间的代码块。
以下是一个简单的示例,演示了如何使用 select
命令创建一个选择菜单:
1 |
|
4. 输入输出
在Shell脚本中,输入输出是非常重要的,它允许您与用户交互并与其他程序进行通信。Shell脚本通常使用标准输入(stdin)、标准输出(stdout)和标准错误(stderr)来处理输入和输出。
4.1 标准输入(stdin)
标准输入通常来自键盘或另一个命令的输出。您可以使用 read
命令从标准输入中读取用户输入。
1 | echo "请输入您的姓名:" |
在这个例子中,read
命令从标准输入中读取用户输入,并将其赋值给变量 name
,然后使用该变量打印出一条问候消息。
4.2 标准输出(stdout)
标准输出通常是您的脚本产生的输出,它默认会显示在终端上。您可以使用 echo
命令来向标准输出中写入内容。
1 | echo "这是一条消息。" |
4.3 标准错误(stderr)
标准错误通常用于输出错误消息和警告。与标准输出类似,标准错误默认也会显示在终端上,但是您可以将其重定向到文件中。
1 | cat non_existent_file 2> error.log |
在这个例子中,cat
命令尝试打开一个不存在的文件,错误消息将被重定向到名为 error.log
的文件中。
4.4 文件重定向
除了标准输入、输出和错误之外,您还可以使用文件重定向来控制输入和输出。以下是一些常见的文件重定向操作符:
>
:将输出重定向到文件中,如果文件不存在则创建,如果存在则覆盖。>>
:将输出追加到文件中,如果文件不存在则创建。<
:将文件内容重定向到命令的输入中。|
:管道操作符,将一个命令的输出作为另一个命令的输入。
1 | echo "这是一条消息。" > output.txt |
在这个例子中,第一条命令将字符串 “这是一条消息。” 写入到 output.txt
文件中,第二条命令将 input.txt
文件的内容发送到 grep
命令,并在其中搜索关键字 “keyword”。
这些是Shell脚本中处理输入输出的基本方法。您可以使用这些方法与用户交互,并与其他程序进行通信。
- 除了使用
read
命令从标准输入中读取用户输入外,您还可以使用重定向符号<
将文件内容重定向到命令的输入中。
1 | while read line; do |
这个例子中,while
循环会从 input.txt
文件中读取每一行,并将其赋值给 line
变量。
4.5 Here Doc 和Here String
在 Shell 脚本中,Here Document(Here Doc)和 Here String 是用于处理输入的两种特殊技术。
1. Here Document(Here Doc)
Here Document 是一种将多行文本作为输入传递给命令或脚本的方法。它使用 <<
操作符,后面跟着一个标记(可以是任何标识符),然后是要输入的文本,直到遇到标记结束。
1 | cat << EOF |
在这个示例中,cat
命令会将 Here Doc 中的文本作为输入,并打印出来。EOF
是结束标记,用于指示 Here Doc 的结束。
2. Here String
Here String 是一种将字符串作为输入传递给命令或脚本的方法。它使用 <<<
操作符,后面跟着要输入的字符串。
1 | grep "pattern" <<< "这是一段输入字符串" |
在这个示例中,<<<
操作符将字符串 “这是一段输入字符串” 作为输入传递给 grep
命令,并在其中搜索指定的模式。
区别:
- Here Document 用于传递多行文本作为输入,而 Here String 用于传递单行字符串作为输入。
- Here Document 使用
<<
操作符,而 Here String 使用<<<
操作符。
这些特殊技术使得 Shell 脚本能够更灵活地处理输入,无论是从文件、变量还是直接输入的文本。
4.6 文件描述符
文件描述符(File Descriptor)是操作系统中用于标识和访问文件或其他I/O资源的整数。在Unix和类Unix系统中,一般情况下有三个标准的文件描述符:
标准输入(stdin): 文件描述符为0。它通常用来表示程序的输入来源,例如键盘输入或者另一个命令的输出。
标准输出(stdout): 文件描述符为1。它通常用来表示程序的输出目标,例如终端输出或者重定向到文件中。
标准错误(stderr): 文件描述符为2。它通常用来表示程序的错误消息输出目标,例如终端输出或者重定向到文件中。
这些标准文件描述符在Shell脚本中非常有用,因为它们允许您控制输入和输出的来源和目标。除了标准文件描述符外,您还可以创建自定义文件描述符来引用其他文件或管道。
创建自定义文件描述符:
在Shell脚本中,您可以使用 exec
命令来创建自定义文件描述符,并将其与文件或管道关联起来。
1 | # 将文件描述符3关联到文件test.txt |
在这个示例中,exec 3< test.txt
将文件描述符3关联到文件 test.txt
,然后可以使用 <&3
从文件描述符3读取数据。最后,使用 exec 3<&-
关闭文件描述符3。
自定义文件描述符可以用于各种用途,如输入重定向、输出重定向、管道等。它们为Shell脚本提供了更灵活的输入输出控制机制。
使用文件描述符重定向命令的输出目标,将输出内容发送到文件或另一个命令中。
1 | # 将文件描述符4关联到文件output.txt,并将命令的标准输出重定向到文件描述符4 |
使用文件描述符来创建管道,将一个命令的输出作为另一个命令的输入。
1 | # 创建管道,将命令1的输出重定向到文件描述符3,然后从文件描述符3读取数据作为命令2的输入 |
在Shell脚本中,您可以使用 ulimit -a
命令来显示当前Shell会话的各种限制,包括文件描述符数量。文件描述符的数量由 open files
或 max user processes
参数表示。
1 | ulimit -a | grep "open files" |
这将显示当前Shell会话中允许打开的最大文件描述符数量。请注意,此处显示的值可能受操作系统和用户权限限制的影响。
4.7 命令替换
命令替换是一种Shell脚本中常用的技术,它允许将命令的输出结果作为字符串嵌入到另一个命令中,从而实现动态构建命令的目的。在Shell中,有两种形式的命令替换:使用反引号(`command`)或使用 $()
。
- 使用反引号进行命令替换:
反引号(`)是一种用于命令替换的特殊字符,可以将其内部的命令执行,并将其输出作为字符串返回。
1 | result=`command` |
- 使用
$()
进行命令替换:$()
是另一种常用的命令替换语法,与反引号的作用相同,但通常更易读、更易于嵌套使用。
1 | result=$(command) |
- 使用示例:
示例1:将命令的输出赋值给变量:
1 | files_count=$(ls | wc -l) |
示例2:在命令中使用命令替换:
1 | echo "当前用户是 $(whoami)。" |
示例3:嵌套使用命令替换:
1 | echo "当前时间是 $(date +%Y-%m-%d_%H:%M:%S)。" |
1 | result=$(ls -l $(dirname $(which bash))) |
1 | # 将命令替换的结果直接作为命令的参数传递给另一个命令。 |
1 | # 利用Shell的控制结构和条件语句,动态地构建命令替换,以根据不同的条件执行不同的命令。 |
1 | # 这个例子中,循环遍历所有的 .txt 文件,并构建对每个文件执行的命令,然后使用 eval 执行每个命令。 |
1 | # 在这个示例中,我们定义了一个名为 servers 的数组,其中包含了多个服务器的名称。然后,我们使用循环遍历数组中的每个服务器,通过SSH连接到服务器,并获取其运行时间。 |
通过结合其他Shell特性,如条件语句、循环、函数和数组等,我们可以将命令替换的结果与其他功能结合起来,从而实现更加灵活和强大的功能。这样可以让我们编写更复杂、更可靠的Shell脚本来满足各种需求。
命令替换的优点:
- 方便实用: 命令替换使得在Shell脚本中处理命令输出变得非常方便,可以直接将命令输出用于后续操作。
- 可读性高: 使用
$()
语法进行命令替换通常比使用反引号更加直观和易读。
注意事项:
- 引号处理: 在命令替换中,通常建议将命令替换语法放在双引号中,以避免由于特殊字符或空白字符而导致的意外行为。
- 嵌套使用: 可以嵌套使用命令替换来构建复杂的命令,但需要注意维护代码的可读性和可维护性。
命令替换是Shell脚本中非常常用且强大的技术,可以帮助您更轻松地处理命令的输出结果,并构建动态的命令。
4.8 进程替换
进程替换(Process Substitution)是一种特殊的Shell操作,允许将命令的输入或输出重定向到临时文件或命名管道中,以便在命令之间传递数据,而无需创建显式的临时文件或管道。它提供了一种简洁而方便的方式来处理需要文件输入或输出的命令。
4.8.1 进程替换的语法:
进程替换使用了尖括号(<()
)或括号(>()
)来表示,后面跟着要执行的命令。
<()
:将命令的输出重定向到临时文件中,并返回该临时文件的路径。>()
:将命令的输入重定向到一个命名管道中,并返回该管道的文件描述符。
使用示例:
- 将命令的输出重定向到临时文件中:
1 | sort <(command1) |
这个命令将 command1
命令的输出重定向到一个临时文件中,然后 sort
命令将该临时文件的内容作为输入进行排序。
1 | # 将命令替换的结果作为文件名参数传递给命令: |
1 | # 将进程替换的结果作为参数传递给函数: |
1 | for file in *.txt; do |
- 将命令的输入重定向到命名管道中:
1 | command1 >(command2) |
这个命令将 command1
命令的输出重定向到一个命名管道中,然后 command2
命令将该管道作为输入进行处理。
4.8.2 进程替换的优点:
- 避免了创建显式的临时文件或管道: 进程替换使得处理需要文件输入或输出的命令变得更加简单和方便,无需手动创建临时文件或管道。
- 效率高: 进程替换利用了操作系统的文件缓存和磁盘读取优化,因此在处理大量数据时可能比手动创建临时文件或管道更高效。
4.8.3 注意事项:
- 临时文件的生命周期: 进程替换创建的临时文件或管道的生命周期与命令执行的时间一样长,命令执行完毕后会自动删除临时文件或管道。
- 不是所有的命令都支持: 并非所有的Shell命令都支持进程替换,某些复杂的命令可能会出现问题或不支持。
进程替换是Shell脚本中一种非常方便的技术,特别适用于需要处理文件输入输出的情况。
5. Shell 函数
当你在编写 Bash 脚本或者在交互式的 Bash Shell 中使用函数时,你实际上是在定义一段可重用的代码块。Shell 函数允许你将一系列命令组合在一起,使得它们可以被多次调用,从而提高了脚本的可读性、可维护性和复用性。
5.1 函数的定义和调用
5.1.1 定义函数
在 Bash 中,你可以使用 function
关键字或者直接使用函数名来定义函数。以下是定义函数的通用语法:
1 | function_name() { |
或者:
1 | function function_name { |
函数名可以包含字母、数字和下划线,但不能以数字开头。
值得注意的是,函数定义通常放在脚本的开头部分,以便在整个脚本中都能够使用它们。
5.1.2 调用函数
一旦定义了函数,你可以通过函数名来调用它。调用函数时,可以像调用其他命令一样直接使用函数名:
1 | function_name |
5.2 函数参数
在 Bash 脚本中,函数可以接受参数,这使得函数更加灵活和通用。函数参数是在调用函数时传递给函数的值,函数可以使用这些值来执行特定的操作。
5.2.1 函数传参
在调用函数时,可以在函数名后面列出参数。参数用空格分隔。
1 | function_name parameter1 parameter2 parameter3 |
5.2.2 函数内部访问参数
在函数内部,可以使用 $1
, $2
, $3
, … 的形式来引用传递给函数的参数。例如,$1
引用第一个参数,$2
引用第二个参数,依此类推。
1 | countfiles() { |
5.2.3 特殊参数
$0
: 当前脚本的名称。$#
: 传递给函数的参数个数。$@
: 传递给函数的所有参数列表。$*
: 传递给函数的所有参数,作为单个字符串
1 | print_all_parameters() { |
5.2.4 参数默认值
设置参数的默认值可以使函数更加灵活,因为它允许函数在不同的情况下具有不同的行为。如果没有默认值,函数可能会对缺少参数的情况做出假设,这可能导致不可预测的结果或错误。
对于具有多个可选参数的函数,设置默认值可以降低调用函数时的复杂性。用户只需提供他们感兴趣的参数,而不必为每个参数都提供值。这样可以简化函数的使用,并使代码更易于理解和维护。
在 Bash 中,为函数参数设置默认值有几种常用的方式:
- 使用变量赋值
1 | greet() { |
在这个例子中,${1:-"world"}
表示如果第一个参数 $1
未被传递或为空,则将 name
变量设置为 "world"
,否则将其设置为传递的参数值。
- 使用条件语句
1 | greet() { |
这里使用 -z
来检查参数是否为空,如果为空,则将 name
设置为 "world"
,否则将其设置为传递的参数值
- 直接使用参数变量
1 | greet() { |
在这个例子中,直接使用 ${1:-}
获取第一个参数,如果参数为空,则将 name
设置为 "world"
。
5.3 局部和全局变量
在 Bash 中,在函数内部不使用 local
关键字定义的变量默认情况下是全局变量。这与其他编程语言的行为有所不同,在其他语言中,未经声明的变量通常是局部变量。
使用局部变量可以避免在函数内部意外地修改全局变量的值,从而提高代码的健壮性。
1 | ➜ greet() { |
在函数内部,如果需要使用全局变量,可以直接访问。任何在函数外部定义的变量都是全局变量,它们对于整个脚本都是可见的。
第二部分:进阶
6. 文件操作
6.1 文件测试
在Shell编程中,文件测试用于检查文件的各种属性,例如文件是否存在、是否是目录、是否可读可写等。在Bash中,可以使用条件语句结合一系列的文件测试来判断文件的属性。
-e
:文件是否存在。-f
:文件是否为普通文件。-d
:文件是否为目录。-r
:文件是否可读。-w
:文件是否可写。-x
:文件是否可执行。-s
:文件是否非空。-z
:文件是否为空。-h
或-L
:文件是否为符号链接。-N
:文件自上次读取以来是否已被修改。
1 | if [ -e "/etc/nginx/nginx.conf" ]; then |
当然还有一些其他测试
检查文件是否是空白文件:
-s
:文件是否有大小(即非空文件)。-z
:文件是否为空(即文件大小为零)。
检查文件的设备号和节点号:
-h
:文件是否是一个符号链接。-r
:文件是否可读。-w
:文件是否可写。-x
:文件是否可执行。
检查文件的权限:
-u
:文件是否设置了 SUID 位(Set User ID)。-g
:文件是否设置了 SGID 位(Set Group ID)。-k
:文件是否设置了粘滞位(Sticky Bit)。
检查文件的类型:
-c
:文件是否为字符设备。-b
:文件是否为块设备。-p
:文件是否为命名管道(FIFO)。-S
:文件是否为套接字文件。
检查文件的最后访问时间和修改时间:
-nt
:文件是否比另一个文件新。-ot
:文件是否比另一个文件旧。
这些文件测试方法可以在脚本编写中进行更全面的文件属性检查。
6.2 文件操作命令(cp
、mv
、rm
等)
文件操作命令用于在Unix/Linux系统中对文件进行各种操作,包括创建、复制、移动、删除、重命名、查看内容等。以下是一些常见的文件操作命令:
6.2.1 创建文件:
touch filename
:创建空文件或更新文件的访问和修改时间戳。如果文件已存在,则它的时间戳会被更新。echo "content" > filename
:将文本内容写入文件。cat > filename
: 使用cat
命令可以创建一个空文件,或者向现有文件中追加内容。输入文本后按Ctrl + D
结束输入。printf "content" > filename
类似于echo
,但更灵活。它可以用来向文件中写入格式化的文本。
6.2.2 复制文件:
cp
是一个常用的 Linux/Unix 命令,用于复制文件或目录。以下是 cp
命令的详解:
1 | cp [选项] 源文件 目标文件或目录 |
常用选项:
-i
:在覆盖目标文件之前提示用户确认。-r
或-R
:递归地复制目录及其内容。-p
:保留文件属性,包括权限、所有者、时间戳等。-a
:递归地复制目录,并保留所有文件属性,等同于-dpR
。-u
:仅在源文件较新时才复制文件。-v
:显示复制的详细信息。-n
:如果目标文件已存在,则不覆盖。-l
:创建硬链接而不是复制文件。-s
:创建符号链接而不是复制文件。-d
:将保留符号链接的属性--backup[=CONTROL]
:在覆盖目标文件之前进行备份
1 | # 将文件复制到目录 |
1 | lrwxr-xr-x 1 user group 15 Feb 21 10:00 source_file -> /path/to/actual_file |
-d
选项保留了符号链接的属性,创建了一个指向同样目标的新的符号链接。
如果省略了-d
选项,cp
命令将会复制符号链接所指向的实际文件或目录,而不是保留符号链接本身。
6.2.3 移动/重命名文件:
mv
用于移动文件或重命名文件或目录
1 | mv [选项] 源文件/目录 目标文件/目录 |
主要选项
-i
:在覆盖前提示确认。-f
:强制执行移动操作,不进行提示。-v
:显示详细的移动过程
1 | # 将多个文件移动到目录 |
6.2.4 删除文件:
rm
用于删除文件或目录
1 | # 基本用法 |
注意
- 没有回收站: 与通过图形界面删除文件不同,
rm
会永久删除文件,而不是将它们移动到回收站。请谨慎使用,因为删除的文件通常无法轻易恢复。 - 不可逆转: 一旦使用
rm
删除文件,通常是无法恢复的。在使用-r
或-f
等选项时,请仔细检查您的命令,以避免意外数据丢失。
6.2.5 查看文件内容:
1. cat
cat
是一个常用的命令行工具,用于显示文件内容,它的名称是 “concatenate” 的缩写。尽管它的主要功能是显示文件内容,但它还可以用于将多个文件连接在一起并输出到标准输出流。
常见选项:
-n, –number:显示输出的行号。
-b, –number-nonblank:仅对非空行编号。
-s, –squeeze-blank:压缩连续的空行为一行。
-E, –show-ends:在每行末尾显示 $
符号。
-T, –show-tabs:将制表符显示为 ^I
。
-v, –show-nonprinting:显示非打印字符,以八进制转义序列形式。
-A, –show-all:等效于 -vET
的组合,显示所有的非打印字符,包括换行符和制表符。
基本用法:
1 | cat [OPTION]... [FILE]... |
- 如果不提供任何文件参数,则
cat
会从标准输入中读取数据并将其输出到标准输出。 - 如果提供一个或多个文件参数,则
cat
会按照它们在命令行中出现的顺序依次读取它们的内容,并将它们连接起来输出到标准输出。
1 | # 1. 显示文件内容: |
cat
是一个简单但功能强大的命令行工具,用于处理文本文件。它在处理小型文件或快速查看文件内容时非常方便,但在处理大型文件时可能会有性能问题。
2. more或less
more filename
或less filename
:逐页查看文件内容。
more
是一个命令行工具,通常用于查看文本文件的内容。它会按页显示文件内容,并等待用户按键来逐页浏览。more
命令的基本语法是:
1 | more [选项] 文件名 |
其中,选项可以是:
-n
:指定每页显示的行数。例如,more -10 filename
将每页显示 10 行内容。
-d
:显示每页内容之前先清除屏幕,然后显示页头信息。
-c
:不进行逐页显示,直接将整个文件内容输出到屏幕。
-s
:静默模式,禁止在底部显示文件名和提示符。
一些常用的 more
命令示例:
1 | `more filename`:显示文件内容,按空格键显示下一页,按 Enter 键显示下一行,按 `q` 键退出。 |
less
1 | 除了基本用法外,`less` 还提供了一些高级功能和快捷键,使得在浏览文本文件时更加灵活和高效。以下是一些 `less` 的详细用法: |
3.head
head filename
:查看文件的前几行。
head
是一个常用的命令行工具,用于显示文本文件的开头部分。以下是关于 head
命令的详解:
基本用法:
1 | head [OPTION]... [FILE]... |
- 如果不指定文件名,则
head
命令会从标准输入中读取数据,并显示开头部分。 - 如果指定了一个或多个文件名,则
head
命令会显示每个文件的开头部分。
常用选项:
-n, –lines=[-]NUM:指定要显示的行数。默认情况下,
head
显示文件的前 10 行。可以使用-n
选项来指定要显示的行数,例如-n 20
将显示前 20 行。-c, –bytes=[-]NUM:指定要显示的字节数。可以使用
-c
选项来指定要显示的字节数,例如-c 100
将显示文件的前 100 个字节。-q, –quiet, –silent:不显示文件名头部信息。当处理多个文件时,可以使用此选项来取消显示每个文件名的头部信息。
-v, –verbose:总是显示文件名的头部信息。与
-q
选项相反,此选项用于强制显示每个文件名的头部信息。-z, –zero-terminated:使用 NUL 作为行分隔符。当处理二进制文件时,可以使用此选项来指定 NUL 字符(ASCII 值为 0)作为行分隔符。
–help:显示帮助信息并退出。
–version:显示版本信息并退出。
tail filename
:查看文件的最后几行。
6.2.6 编辑文件:
nano filename
或vi filename
:使用文本编辑器编辑文件。
vim
Vim 是一个强大的文本编辑器,它在命令行中运行,并提供了许多快捷键和命令来快速编辑文本文件。以下是一些基础的 Vim 使用方法:
进入 Vim: 在命令行中输入
vim
,然后按 Enter 键。基本移动:
- 使用箭头键可以进行上下左右移动。
h
、j
、k
、l
分别代表左、下、上、右移动。
模式:
- 正常模式 (Normal Mode): 这是默认模式,用于移动光标和执行命令。
- 插入模式 (Insert Mode): 在此模式下,您可以输入文本。按下
i
键进入插入模式。 - 可视模式 (Visual Mode): 用于选择文本。按下
v
键进入可视模式。
保存和退出:
- 在正常模式下,按下
:w
保存文件。 - 按下
:q
退出 Vim。 - 若要保存并退出,可以组合使用:
:wq
。
- 在正常模式下,按下
撤销和重做:
- 在正常模式下,按下
u
撤销上一步操作。 - 若要重做,按下
Ctrl + r
。
- 在正常模式下,按下
删除和复制:
- 在正常模式下,
x
删除光标所在的字符。 dd
删除整行。yy
复制整行。
- 在正常模式下,
粘贴:
- 将文本粘贴到光标处,按下
p
。
- 将文本粘贴到光标处,按下
Vim 的进阶技巧:
搜索和替换:
- 在正常模式下,按下
/
进入搜索模式,输入要搜索的内容,然后按 Enter。使用n
和N
在搜索结果之间进行导航。 - 替换命令使用
:%s/old/new/g
,其中old
是要替换的内容,new
是替换后的内容。:wq
保存并退出。
- 在正常模式下,按下
分屏编辑:
- 使用
:split
或:vsplit
在水平或垂直方向分割窗口。 - 使用
Ctrl + w
然后是h
、j
、k
、l
在窗口之间进行移动。
在 Vim 中取消分屏可以通过以下方法之一完成:
关闭当前窗口:
- 如果你只是想关闭当前窗口而不是取消分屏,可以在要关闭的窗口中执行
:q
命令。这将关闭当前窗口但不会退出 Vim。 - 如果想关闭所有窗口并退出 Vim,可以在每个窗口中执行
:qall
或:qa
命令。
- 如果你只是想关闭当前窗口而不是取消分屏,可以在要关闭的窗口中执行
合并窗口:
- 如果你想要将多个窗口合并成一个,可以使用
:only
命令。执行该命令将关闭除当前窗口外的所有其他窗口,从而实现取消分屏的效果。
- 如果你想要将多个窗口合并成一个,可以使用
手动调整窗口大小:
- 如果想要调整窗口大小以达到取消分屏的效果,可以使用
Ctrl + w
前缀键结合+
、-
、<
或>
来调整当前活动窗口的大小。例如,Ctrl + w +
将增加当前窗口的高度,Ctrl + w >
将增加当前窗口的宽度。
- 如果想要调整窗口大小以达到取消分屏的效果,可以使用
使用上述方法之一可以很容易地取消 Vim 中的分屏。
- 使用
标记和跳转:
- 使用
m
命令标记位置,例如ma
标记为位置a
。 - 使用
'
或`
跳转到标记的位置,例如'a
或`a
跳转到位置a
。
- 宏录制和回放:
- 按下
q
开始录制宏,然后选择一个字母作为宏的名称,例如qa
。 - 在录制期间执行一系列命令,然后按下
q
停止录制宏。 - 使用
@a
来执行刚刚录制的宏。
- 自动补全:
- 在插入模式下,按下
Ctrl + n
或Ctrl + p
进行自动补全。
- 自定义配置:
- 编辑
~/.vimrc
文件来自定义 Vim 的行为,比如设置缩进、主题等。
- 插件管理:
- 使用插件管理器如 Vundle、Pathogen 或 Vim-Plug 来安装和管理插件,扩展 Vim 的功能。
这些是 Vim 的一些进阶技巧,能够使你更加高效地使用 Vim 来编辑文本。继续练习和探索,你会发现 Vim 的强大之处。
当然,请继续学习 Vim 的进阶技巧:
粘贴:
- 在正常模式下:
- 按下
p
将剪贴板中的内容粘贴到光标后。 - 按下
P
将剪贴板中的内容粘贴到光标前。
- 按下
- 在插入模式下:
- 退出插入模式(按下
Esc
)后,按下Ctrl + r
然后是+
来粘贴剪贴板中的内容。
- 退出插入模式(按下
- 在正常模式下:
撤销和重做:
- 撤销:
- 在正常模式下,按下
u
来撤销上一步操作。 - 可以多次按下
u
来连续撤销多个操作。
- 在正常模式下,按下
- 重做:
- 在正常模式下,按下
Ctrl + r
来重做被撤销的操作。
- 在正常模式下,按下
- 撤销:
折叠:
- 使用折叠功能可以将文本按照一定的规则折叠起来,使得文本结构更加清晰。
- 操作命令:
zc
折叠当前光标所在的折叠。zo
展开当前光标所在的折叠。zC
递归折叠,即折叠当前及所有嵌套的折叠。zO
递归展开,即展开当前及所有嵌套的折叠。zr
打开所有折叠。zm
关闭所有折叠。
多文件编辑:
- 使用
:e filename
打开另一个文件进行编辑。 - 使用
:ls
查看当前打开的文件列表。 - 使用
:bnext
或:bprev
切换到下一个或上一个文件。
- 使用
查看帮助:
- 在 Vim 中,可以使用
:help
命令查看帮助文档,如:help motion
将显示有关移动命令的帮助信息。 - 若要关闭帮助窗口,请按下
Ctrl + w
。
- 在 Vim 中,可以使用
多文件编辑:
- 使用
:e filename
命令可以打开另一个文件进行编辑,这将在当前窗口打开新文件。 - 使用
:sp filename
或:vsp filename
命令可以在水平或垂直方向分割窗口并打开另一个文件。 - 使用
:bn
和:bp
命令在多个缓冲区之间切换文件。
让我们继续学习 Vim 的更多功能:
标签页操作:
- 使用
:tabnew
命令可以打开一个新的标签页。 - 使用
gt
切换到下一个标签页,使用gT
切换到上一个标签页。 - 使用
:tabclose
关闭当前标签页,使用:tabcloseall
关闭所有标签页。
- 使用
文本对象操作:
- Vim 提供了许多文本对象,可以使编辑更加精确。
- 例如,
aw
表示一个单词,as
表示一个句子,ap
表示一个段落。 - 你可以在普通模式下结合操作符和文本对象进行操作,比如
daw
删除一个单词,ci"
修改引号内的内容。
会话管理:
- 使用
:mksession
命令可以保存当前会话状态。 - 使用
vim -S Session.vim
命令可以恢复之前保存的会话。
- 使用
窗口缩放:
- 使用
Ctrl + w
后跟>
或<
可以增加或减少当前窗口的宽度。 - 使用
Ctrl + w
后跟+
或-
可以增加或减少当前窗口的高度。
- 使用
撤销树:
- 使用
:GundoToggle
命令可以打开撤销树视图,它显示了所有的撤销历史记录,并允许你在历史记录中进行导航。
- 使用
文件浏览器:
- 使用
:Explore
命令可以打开文件浏览器,查看当前目录中的文件。 - 使用
:Vexplore
可以在垂直分割的窗口中打开文件浏览器。
- 使用
这些是一些更加高级和专业的 Vim 功能,能够让你更加高效和方便地进行文本编辑和管理。继续学习并不断练习,你会成为一个熟练的 Vim 用户。
当然,请继续学习以下关于 Vim 的功能和技巧:
- 多窗口跳转:
- 使用
Ctrl + w
后跟w
可以在不同窗口之间进行切换。 - 使用
Ctrl + w
后跟h
、j
、k
、l
可以在窗口之间进行快速移动。
- 标签页与窗口间跳转:
- 使用
:tabnext
或gt
可以在不同标签页之间进行切换。 - 使用
:tabprevious
或gT
可以切换到前一个标签页。
- 寄存器操作:
- 使用
:reg
命令可以查看当前所有寄存器的内容。 - 使用
"ay
可以将选中的内容复制到寄存器a
中,使用"ap
可以将寄存器a
的内容粘贴出来。
- 自动命令:
- 在
~/.vimrc
中可以设置自动命令,以便在特定事件发生时自动执行某些操作。 - 例如,可以使用
autocmd BufRead *.txt set tabstop=4
来设置在读取.txt
文件时将 tab 键设为 4 个空格。
- 片段插入:
- 使用插件如 UltiSnips 或 SnipMate 可以方便地插入代码片段,提高编码效率。
- 定义好的代码片段可以通过触发关键词后自动展开为完整代码块。
- 列编辑模式:
- 在正常模式下,按下
Ctrl + v
进入列编辑模式,可以对多行文本的某一列进行编辑。 - 在列编辑模式下,可以使用方向键或
h
、j
、k
、l
键选择文本区域,然后进行编辑。
- 命令行模式下的搜索:
- 在命令行模式下,使用
/
可以进行文件内容的搜索,而且支持正则表达式。 - 搜索完成后,按下 Enter 键可以跳转到搜索结果的第一个匹配处。
这些是更进一步的 Vim 功能和技巧,它们可以帮助你更加高效地编辑和管理文本。继续学习和练习,你会变得更加熟练和自信地使用 Vim。
好的,我们继续学习以下关于 Vim 的功能和技巧:
- 文本过滤:
- 使用
:!
命令可以在 Vim 中执行外部命令,并将其输出结果插入到当前文本中。 - 例如,
:!ls
可以列出当前目录的文件列表,并将结果插入到当前文本中。
- 快速注释:
- 使用插件如 NERDCommenter 可以快速对选中的行或当前行进行注释/取消注释操作。
- 使用
gcc
可以注释当前行,使用gc
可以取消注释选中的行。
- 文本收起:
- 使用插件如 vim-indent-guides 可以将文本按缩进进行收起,使得大段的代码更易于阅读。
- 使用
:set foldmethod=indent
命令开启基于缩进的收起功能。
- 查找替换的确认模式:
- 使用
:s/old/new/gc
可以在执行全局替换时启用确认模式,每次替换前都会进行确认。
- 多光标编辑:
- 使用插件如 vim-multiple-cursors 可以在文本中同时操作多个光标,提高编辑效率。
- 通过快捷键触发多个光标,并同时对它们进行编辑。
- Markdown 预览:
- 使用插件如 vim-markdown-preview 可以在 Vim 中实时预览 Markdown 格式的文档。
- 文件类型检测:
- Vim 可以根据文件内容自动检测文件类型,并相应地应用适当的语法高亮和缩进规则。
- 屏幕分页:
- 使用
Ctrl + f
向前翻页,使用Ctrl + b
向后翻页,可以在大文本文件中快速浏览。
这些是一些更高级的 Vim 功能和技巧,可以帮助你更加高效地进行文本编辑和管理。不断学习和练习,你会成为一个熟练的 Vim 用户。
当然,请继续学习以下关于 Vim 的功能和技巧:
- 书签操作:
- 使用
m{a-zA-Z}
命令可以创建书签。例如,使用ma
在当前位置创建书签a
。 - 使用
'a
或`a
可以跳转到书签a
的位置。
- 文件对比:
- 使用 Vim 的内置命令
vimdiff file1 file2
可以进行文件对比,显示两个文件之间的差异。
- 快速移动到行首和行尾:
- 在普通模式下,按下
0
可以快速移动到当前行的行首,按下$
可以快速移动到当前行的行尾。
- 查找当前单词:
- 在普通模式下,将光标放置在要查找的单词上,然后按下
*
可以快速查找当前单词。
- 隐藏和显示行号:
- 使用
:set nu
可以显示行号,使用:set nonu
可以隐藏行号。
- 自动缩进:
- 使用
:set autoindent
可以开启自动缩进功能,使得新行的缩进与上一行相同。
- 自动保存:
- 使用
:set autowrite
可以开启自动保存功能,使得在执行某些操作(如切换缓冲区或退出 Vim)时自动保存文件。
- 窗口自动调整大小:
- 使用
Ctrl + w =
可以使得所有窗口等宽等高。 - 使用
Ctrl + w |
可以使得当前窗口宽度最大化。 - 使用
Ctrl + w _
可以使得当前窗口高度最大化。
- 备份文件和交换文件:
- Vim 可以在编辑文件时创建备份文件(以
filename~
格式保存在同一目录下)和交换文件(以.filename.swp
格式保存在同一目录下)。
这些是一些更高级的 Vim 功能和技巧,能够让你更加灵活和高效地进行文本编辑和管理。继续学习和练习,你会成为一个熟练的 Vim 用户。
好的,让我们继续学习以下关于 Vim 的功能和技巧:
- 快速跳转到指定行:
- 在普通模式下,使用
:{line number}
可以快速跳转到指定行号。例如,:25
将光标移动到第 25 行。
- 查找并替换当前行内容:
- 在普通模式下,使用
:%s/old/new/gc
可以查找并替换当前行的内容。在确认模式下 (c
选项),你可以逐个确认是否替换。
- 编辑命令历史记录:
- 在命令行模式下,使用方向键可以浏览之前执行过的命令,这样你就可以轻松重复执行之前的命令。
- 全屏模式:
- 在普通模式下,使用
:set fullscreen
可以进入全屏模式,最大化 Vim 窗口。
- 窗口高亮:
- 在普通模式下,使用
:set cursorline
可以高亮当前光标所在行。
- 会话保存和加载:
- 使用
:mksession sessionname.vim
可以将当前会话保存为一个文件。 - 使用
vim -S sessionname.vim
可以加载之前保存的会话。
- 文件编码设置:
- 在 Vim 中可以使用
:set fileencoding=utf-8
来设置文件编码格式。
- 文件类型切换:
- 在普通模式下,使用
:set filetype=javascript
可以将当前文件类型设置为 JavaScript,从而启用相应的语法高亮和缩进规则。
- 自动对齐:
- 在插入模式下,使用
Ctrl + ]
可以快速对齐当前行的缩进。
- 分页导航:
- 在命令行模式下,使用
:set mouse=a
可以启用鼠标滚动来进行文本分页导航。
这些是更加高级的 Vim 功能和技巧,能够让你在编辑文本时更加方便和高效。继续学习并不断练习,你会变得更加熟练地使用 Vim。
继续学习以下关于 Vim 的功能和技巧:
- 查找当前文件中的括号匹配:
- 在普通模式下,将光标放在一个括号上,然后按下
%
键可以快速跳转到与之匹配的括号处。
- 设置和查看折行(换行):
- 使用
:set wrap
可以开启折行功能,使得长行在窗口右边折行显示。 - 使用
:set nowrap
可以关闭折行功能。 - 使用
:set wrap?
可以查看当前折行设置的状态。
- 复制、剪切和粘贴到系统剪贴板:
- 在普通模式下,使用
"+y
可以复制文本到系统剪贴板。 - 使用
"+x
可以剪切文本到系统剪贴板。 - 在插入模式下,使用
Ctrl + Shift + v
可以粘贴系统剪贴板中的内容。
- 设置文件保存时自动格式化代码:
在
~/.vimrc
中添加如下配置可以在保存文件时自动格式化代码:1
autocmd BufWritePre *.js,*.html,*.css,*.cpp :normal gg=G
这个命令会在保存 JavaScript、HTML、CSS 和 C++ 文件之前,执行
gg=G
命令,对整个文件进行自动格式化。
- 使用宏在多行上执行相同的操作:
- 在普通模式下,可以使用宏来在多行上执行相同的操作。首先录制宏,然后在需要的地方执行宏。例如,录制宏:
qq0ywA;<Esc>pq
,然后执行宏@q
在多个行的行首添加分号并跳到下一行。
- 自定义代码折叠:
- 使用
:set foldmethod=manual
可以启用手动折叠模式,然后使用zf
和zd
来手动创建和删除折叠。
这些是更加高级的 Vim 功能和技巧,能够让你在编辑文本时更加方便和高效。继续学习并不断练习,你会成为一个熟练的 Vim 用户。
继续学习以下关于 Vim 的功能和技巧:
- 使用缩写来简化输入:
- 在 Vim 中可以使用缩写来简化输入。例如,使用
:ab
命令定义缩写。比如,:ab omw on my way
将会将omw
自动替换为on my way
。
- 调整窗口大小并保持纵横比:
- 在分屏模式下,可以使用
Ctrl + W
后跟>
或<
来调整窗口大小。但是如果你想保持纵横比,可以在命令前加上z
。例如,Ctrl + W z>
或Ctrl + W z<
。
- 在 Vim 中执行 Shell 命令:
- 在普通模式下,使用
:!
后跟 Shell 命令可以在 Vim 中执行任何 Shell 命令。例如,:!ls
将会列出当前目录的文件列表。
- 标签页缩放:
- 使用
:tabmove {N}
命令可以将当前标签页移动到指定位置。其中{N}
是要移动到的位置索引。索引从 0 开始,0 表示最左边的标签页。
- 快速跳转到文件末尾:
- 在普通模式下,使用
G
快捷键可以快速跳转到文件的末尾。
- 自定义快捷键映射:
- 在
~/.vimrc
文件中,你可以自定义快捷键映射来执行一系列 Vim 命令。例如,:nnoremap <leader>w :w<cr>
可以将快捷键\w
映射到保存文件的命令。
- 使用数字重复操作:
- 在普通模式下,可以使用数字来重复一些操作。例如,
2dd
将会删除当前行以及下一行的内容。
- 查看文件状态:
- 使用
:ls
命令可以查看当前打开的所有缓冲区(文件)的列表。
- 跳转到文件末尾并进入插入模式:
- 在普通模式下,使用
G
快捷键将光标跳转到文件末尾,然后再按下o
进入插入模式,光标将会出现在文件末尾的下一行。
这些是更加高级的 Vim 功能和技巧,能够让你在编辑文本时更加方便和高效。不断学习和练习,你会变得更加熟练地使用 Vim。
继续学习以下关于 Vim 的功能和技巧:
- 在 Vim 中执行宏命令:
- 如果你已经录制了一个宏,可以使用
@{register}
来执行该宏。例如,如果你录制了一个宏并将其保存在寄存器a
中,你可以使用@a
来执行它。
- 自动补全功能的使用:
- 在插入模式下,使用
Ctrl + x
后跟Ctrl + n
可以进行关键词补全,根据当前文件中的文本内容进行自动补全。
- 使用
gf
命令快速打开文件:
- 在普通模式下,将光标移动到一个文件路径上,然后按下
gf
快捷键可以快速打开该文件。
- 设置文件编码格式:
- 使用
:set fileencoding=utf-8
命令可以设置当前文件的编码格式为 UTF-8。你也可以根据需要设置其他编码格式。
- 标记跳转到上次编辑位置:
- 在普通模式下,按下
快捷键可以跳转到上次编辑的位置。这个功能对于在文件中不同位置之间快速切换很有用。
- 快速切换到上一个缓冲区:
- 在普通模式下,按下
Ctrl + 6
快捷键可以快速切换到上一个缓冲区。
- 在 Vim 中执行 Shell 命令并将结果插入到文本中:
- 在普通模式下,使用
:r !{command}
可以在 Vim 中执行 Shell 命令,并将其结果插入到当前光标位置之后。
- 自动保存文件的设定:
- 使用
:set autowrite
可以在退出 Vim 之前自动保存文件,以确保在退出 Vim 时不会丢失未保存的更改。
- 单词拼写检查:
- 在普通模式下,使用
z=
快捷键可以对光标下的单词进行拼写检查,并提供可能的修正建议。
- 使用 Vimdiff 进行文件比较:
- 使用
vimdiff file1 file2
命令可以在 Vim 中启动文件比较模式,方便你查看并编辑两个文件的差异。
这些是更加高级的 Vim 功能和技巧,能够让你在编辑文本时更加方便和高效。继续学习和练习,你会变得更加熟练地使用 Vim。
继续学习以下关于 Vim 的功能和技巧:
- 在 Vim 中执行 Python 脚本:
- 在普通模式下,使用
:!python script.py
命令可以在 Vim 中执行 Python 脚本script.py
,并在 Vim 中查看输出结果。
- 使用
Ctrl + o
和Ctrl + i
在位置之间快速切换:
- 在普通模式下,使用
Ctrl + o
可以向后跳转到上一个光标位置,而Ctrl + i
则可以向前跳转到下一个光标位置。
- 使用
:sort
对文本进行排序:
- 在命令行模式下,使用
:sort
命令可以对当前光标所在位置以下的文本进行排序。例如,:sort
可以按字母顺序对文本进行排序。
- 使用
viminfo
文件保存 Vim 会话信息:
- Vim 使用
viminfo
文件来保存会话信息,包括历史命令、搜索历史、标记位置等。你可以通过修改~/.vimrc
文件来自定义viminfo
文件的保存路径和设置。
- 使用
zz
将光标所在行置于屏幕中央:
- 在普通模式下,使用
zz
快捷键可以将光标所在行置于屏幕中央,方便查看光标所在行的上下文内容。
- 在插入模式中使用
Ctrl + w
删除前一个单词:
- 在插入模式下,使用
Ctrl + w
快捷键可以删除光标前的一个单词,这对于纠正输入错误或者删除不需要的内容很有帮助。
- 在普通模式下使用
gv
重新选中上次的高亮选区:
- 在普通模式下,按下
gv
快捷键可以重新选中上次的高亮选区,方便你对选中的文本执行其他操作,比如复制、剪切或者编辑。
- 使用
Ctrl + a
和Ctrl + x
增加或减少数字:
- 在插入模式下,按下
Ctrl + a
可以增加光标位置所在数字的值,而Ctrl + x
则可以减少数字的值,这在编辑数字序列时非常方便。
- 在 Vim 中设置行间距:
在
~/.vimrc
文件中添加如下设置可以设置 Vim 编辑器的行间距:1
:set linespace=4
这会将行间距设置为 4 个像素,你可以根据需要调整行间距的大小。
这些是更多关于 Vim 的高级功能和技巧,可以帮助你更加高效地进行文本编辑。继续学习和练习,你会变得更加熟练地使用 Vim。
继续学习以下关于 Vim 的功能和技巧:
- 使用
Ctrl + g
查看文件信息:
- 在普通模式下,按下
Ctrl + g
可以显示当前文件的信息,包括文件名、行号以及文件状态等。
- 在普通模式下快速插入新行:
- 在普通模式下,按下
o
可以在光标所在行的下方插入新的一行,并进入插入模式。 - 按下
O
可以在光标所在行的上方插入新的一行,并进入插入模式。
- 使用
:set relativenumber
切换相对行号:
- 使用
:set relativenumber
命令可以切换为相对行号模式,该模式下每行显示相对于光标位置的行号。
- 使用
Ctrl + x
进行补全:
- 在插入模式下,按下
Ctrl + x
后,再按下Ctrl + f
可以根据当前光标位置的单词补全文件名。
- 在 Vim 中执行 Ex 命令:
- 在普通模式下,输入
:
进入命令行模式,然后输入 Ex 命令,例如:w
保存文件,q
退出 Vim。
- 使用
:g
命令执行全局操作:
- 使用
:g/pattern/cmd
可以在文件中查找匹配pattern
的所有行,并对其执行cmd
命令。例如,:g/TODO/d
将删除所有包含 “TODO” 的行。
- 使用
:earlier
和:later
命令在时间轴上移动:
- 使用
:earlier {time}
命令可以将当前文件恢复到{time}
之前的状态。而:later {time}
可以将当前文件恢复到{time}
之后的状态。
- 使用
:source
命令执行脚本文件:
- 使用
:source {file}
命令可以执行指定的脚本文件,该文件中包含了 Vim 命令或者配置。
- 使用
vim -u NONE
进入纯净模式:
- 在启动 Vim 时加上
-u NONE
参数可以进入纯净模式,不加载任何插件和配置文件,用于排除配置问题。
- 使用
:only
命令关闭其他窗口:
- 使用
:only
命令可以关闭除当前窗口外的所有其他窗口,使得当前窗口独占显示。
这些是更多关于 Vim 的高级功能和技巧,可以帮助你更加高效地进行文本编辑。继续学习和练习,你会变得更加熟练地使用 Vim。
当然,请继续学习以下关于 Vim 的功能和技巧:
- 使用
:set list
显示不可见字符:
- 使用
:set list
命令可以显示文档中的不可见字符,例如空格、制表符等。这对于调试格式化问题非常有用。
- 使用
:sort u
对文本进行唯一排序:
- 使用
:sort u
命令可以对文本进行唯一排序,去除重复行并保持排序顺序不变。
- 在普通模式下移动到当前文件的顶部和底部:
- 使用
gg
快捷键可以将光标快速移动到文件的第一行。 - 使用
G
快捷键可以将光标快速移动到文件的最后一行。
- 在命令行模式下执行多个命令:
- 在命令行模式下,使用竖线
|
分隔多个命令可以一次执行多个命令。例如,:w | !python %
可以保存当前文件并在 Python 中执行它。
- 使用
:retab
命令重新缩进整个文件:
- 使用
:retab
命令可以根据当前的tabstop
和shiftwidth
设置重新缩进整个文件。
- 在搜索模式下使用
\zs
和\ze
定位匹配部分:
- 在正则表达式中,使用
\zs
表示匹配的起始位置,使用\ze
表示匹配的结束位置。这对于定位匹配的特定部分很有用。
- 使用
:source
命令加载外部脚本:
- 使用
:source {file}
命令可以加载并执行外部 Vim 脚本文件,这样可以将一些常用的配置或功能封装在脚本中,方便重用。
- 在 Vim 中执行宏并保存到寄存器:
- 使用
qa
开始录制宏并将其保存到寄存器a
中,然后执行一系列操作,最后使用q
结束录制。
- 在 Vim 中设置自动缩进:
- 在
~/.vimrc
文件中添加:set autoindent
可以开启自动缩进功能,使得新行的缩进与上一行相同。
- 使用
:sp
和:vsp
打开水平或垂直分割窗口:
- 使用
:sp {file}
可以水平分割当前窗口并打开一个新文件。而:vsp {file}
则可以垂直分割当前窗口。
这些是更多关于 Vim 的高级功能和技巧,能够让你在编辑文本时更加方便和高效。继续学习和练习,你会变得更加熟练地使用 Vim。
1 | :%s/\w\+/\U\&\E/g #将多个英文字符替换成大写 |
6.2.7 查找文件:
find directory -name filename
:在指定目录及其子目录中查找文件。grep pattern filename
:在文件中查找匹配指定模式的行。
6.2.8 文件权限和所有权:
chmod permissions filename
:修改文件的权限。chown user:group filename
:修改文件的所有者和所属组。
6.2.9 压缩和解压文件:
gzip filename
或gzip -d filename.gz
:压缩或解压文件。tar -cvf archive.tar files
:创建 tar 归档文件。tar -xvf archive.tar
:解压 tar 归档文件。
6.2.10 其他操作:
- `ln -s source_file symbolic_link`:创建符号链接。
- `wc filename`:统计文件的行数、单词数和字符数。
- `sort filename`:对文件内容进行排序。
- `uniq filename`:去除文件中的重复行。
这些文件操作命令可以帮助你在Unix/Linux系统中对文件进行各种常见的操作。
6.3 文件权限和用户
7. 正则表达式和模式匹配
7.1 正则表达式基础
7.2 使用 grep
进行模式匹配
7.3 使用 sed
进行文本处理
7.3.1 sed工作原理
sed
(Stream Editor)是一个流编辑器,它以逐行方式处理输入流(通常是文本文件),并根据指定的编辑命令进行相应的操作。它基于一种简单而强大的编辑语言,允许用户执行搜索、替换、删除、插入等多种操作,从而对文本进行灵活的处理。
sed
的工作原理如下:
逐行处理输入流:
sed
逐行读取输入流,并对每一行应用相应的编辑命令。输入流可以是文件、管道输出等。模式匹配:
sed
使用模式来匹配文本中的特定部分。用户可以使用正则表达式或简单的字符串来定义匹配模式。编辑命令:
sed
使用编辑命令来对匹配到的文本进行操作。编辑命令可以是替换命令、删除命令、插入命令等,以及一些控制流程的命令。应用编辑命令: 一旦匹配到了指定的文本,
sed
就会根据相应的编辑命令执行相应的操作。这些操作可以是替换匹配文本、删除匹配行、插入新文本等。输出结果:
sed
在处理完所有输入流后,将结果输出到标准输出(通常是终端),或者通过-i
参数直接在原文件上进行修改。
总的来说,sed
通过一系列的模式匹配和编辑命令,实现了对文本的灵活处理和编辑。它是一种非常强大且灵活的文本处理工具,适用于各种文本编辑和处理场景。
7.3.2 sed空间
在 sed
中,有两个特殊的空间:模式空间(pattern space)和保留空间(hold space)。这些空间允许在处理文本时存储和操作数据。
模式空间(pattern space): 模式空间是
sed
中用于存储当前处理的行的缓冲区。当sed
读取一行文本时,文本会被存储在模式空间中,sed
在模式空间中对其进行匹配和操作。模式空间中的内容可以被修改、替换或者通过命令输出到标准输出。默认情况下,sed
在处理完每一行后会自动清空模式空间,然后读取下一行。保留空间(hold space): 保留空间是另一个可用于存储数据的空间,与模式空间相互独立。可以使用保留空间来存储临时数据或在处理文本时进行比较。与模式空间不同,保留空间的内容不会随着处理的文本行而变化。
通过在 sed
脚本中使用特定的命令,可以将数据从模式空间移动到保留空间,反之亦然。这使得 sed
能够在文本处理过程中灵活地使用和操作数据。
以下是一些 sed
命令,用于操作模式空间和保留空间中的数据:
h
:将模式空间中的内容复制到保留空间。H
:将模式空间中的内容追加到保留空间的末尾。g
:将保留空间中的内容复制到模式空间。G
:将保留空间中的内容追加到模式空间的末尾。x
:交换模式空间和保留空间中的内容。
通过合理利用模式空间和保留空间,可以实现更复杂的文本处理任务,例如在 sed
中执行高级的文本转换、替换和过滤操作。
除了模式空间(Pattern Space)和保留空间(Hold Space)外,还有以下几个空间或概念:
输入缓冲区(Input Buffer):这是
sed
从输入文件或标准输入读取当前行的地方。在读取一行后,sed
会将其放入模式空间中进行处理。输出缓冲区(Output Buffer):在默认情况下,
sed
会将处理后的模式空间内容立即输出。但是在某些情况下(如使用-n
选项时),输出会被暂存,直到明确调用p
或其他输出指令,或者处理结束时才会输出。命令行缓冲区(Command Line Buffer):
sed
脚本中的命令会被暂存到这个缓冲区中,随后按顺序应用于模式空间的内容。每一行在处理完毕后,sed
会继续读取下一行并重复这个过程。地址范围和上下文范围(Address and Context Range Buffers):当指定一个地址范围或上下文范围(如
sed '/start/,/end/'
)时,sed
会记住当前行是否在该范围内,以决定是否应用接下来的命令。n
:将下一行输入读取到模式空间,但不执行后续命令。N
:将下一行输入追加到当前模式空间的末尾。P
:仅打印模式空间中的第一行文本。D
:删除模式空间中的第一行文本,然后重新开始处理剩余的文本。
7.3.3 sed命令格式
sed
命令的基本格式如下:
1 | sed [选项] '编辑命令' 文件名 |
其中,选项是可选的,可以用来控制 sed
的行为,编辑命令是对文件进行处理的命令,文件名是要处理的文本文件。
常用的选项包括:
-n
:关闭自动打印模式,只有经过编辑命令处理后的行才会输出。-e
:指定一个或多个编辑命令。-f
:从指定文件中读取编辑命令。
编辑命令是对文本进行操作的指令,可以是替换、删除、插入、打印等操作,常见的编辑命令包括:
s/old/new/g
:替换行中的old
字符串为new
。/pattern/d
:删除匹配到的行。/pattern/i\text
:在匹配到的行之前插入文本。/pattern/a\text
:在匹配到的行之后追加文本。/pattern/p
:打印匹配到的行。
例如,要将文件中的所有 foo
替换为 bar
,可以使用以下命令:
1 | sed 's/foo/bar/g' filename |
如果要删除所有包含 pattern
的行,并将结果输出到新文件中,可以使用 -n
和 /pattern/d
选项:
1 | sed -n '/pattern/d' filename > newfile |
以上是 sed
命令行的基本格式和用法,通过组合不同的选项和编辑命令,你可以实现各种文本处理操作。
7.3.4 sed指令
sed
编辑命令用于对文本进行操作,包括替换、删除、插入、追加、打印等。以下是常用的 sed
编辑命令及其说明:
替换命令 (
s
):s/old/new/g
:将行中所有的old
替换为new
。其中g
表示全局替换,如果不加g
,则只替换每行的第一个匹配。除了g标志,还可以是具体数字:s/old/new/2,表示替换第2个。- p标志打印替换的行:s/old/new/p。
- w标志可以将更改的行保存的文件中:s/old/new/w file.txt。
- i标志表示不区分大小写:s/old/new/i
- 例如:
sed 's/foo/bar/g' filename
:将文件中所有的foo
替换为bar
。
s命令除了可以使用/作为分隔符,还可以使用**| @ ! ^**作为分隔符
字符替换标志:
\L: 将紧随其后的所有字符转换为小写,直到遇到
\E
或字符串结束。\U: 将紧随其后的所有字符转换为大写,直到遇到
\E
(结束转换)或字符串结束。\l: 将紧随其后的第一个字符转换为小写。
\u: 将紧随其后的第一个字符转换为大写。
\E:用于结束
\L
或\U
的作用范围。删除命令 (
d
):/pattern/d
:删除匹配到的行。- 例如:
sed '/pattern/d' filename
:删除所有包含pattern
的行。
插入命令 (
i
):/pattern/i\text
:在匹配到的行之前插入指定文本。- 例如:
sed '/pattern/i\new_text' filename
:在包含pattern
的行之前插入new_text
。
追加命令 (
a
):/pattern/a\text
:在匹配到的行之后追加指定文本。- 例如:
sed '/pattern/a\new_text' filename
:在包含pattern
的行之后追加new_text
。
打印命令 (
p
):/pattern/p
:打印匹配到的行。- 例如:
sed -n '/pattern/p' filename
:仅打印包含pattern
的行。
替换命令 (
y
):y/chars1/chars2/
:将文本中chars1
中的字符替换为chars2
中对应位置的字符。- 例如:
sed 'y/abc/123/' filename
:将文件中所有的a
替换为1
,b
替换为2
,c
替换为3
。
除了上述提到的常用编辑命令外,sed
还有一些其他常用的编辑命令,包括:
行匹配命令 (
\
):/pattern/\<命令>
:对匹配到的行执行指定的命令。- 例如:
sed '/pattern/{s/foo/bar/g}' filename
:在包含pattern
的行中,将所有的foo
替换为bar
。
行范围命令 (
start,end
):start,end\<命令>
:对指定行范围内的所有行执行指定的命令。- 例如:
sed '2,4{s/foo/bar/g}' filename
:对第 2 行到第 4 行之间的所有行,将所有的foo
替换为bar
。
行范围(start,+num):
从start开始的,num行
行范围(start~num):
从start行开始,每num行。注意和**(start,~num)**的区别
/pattern/,/pattern/或者start,/pattern/也可以混合地址和模式/pattern/,+num
行号命令 (
num
):num\<命令>
:对指定行号的行执行指定的命令。- 例如:
sed '2{s/foo/bar/g}' filename
:对第 2 行,将所有的foo
替换为bar
。
行尾追加命令 (
a\
):$a\text
:在每行的行尾追加指定文本。- 例如:
sed '$a\End' filename
:在每行的行尾追加End
。
行删除命令 (
d
):numd
:删除指定行号的行。- 例如:
sed '2d' filename
:删除第 2 行。
替换行命令(c):
替换整行
显示隐藏字符(l)
1
2sed -n 'l' wawa.txt
sed -n 'l 5' wawa.txt #显示了个字符后换行退出当前执行流程(q)
请注意,quit 命令不接受地址范围,它仅支持单个地址。默认情况下,SED 遵循读取、执行和重复工作流程;但是当遇到quit命令时,它只是停止当前的执行。
1
2
3
4
5
6
7
8
9
10
11
12
13[root@MiWiFi-RA80-srv test]# sed '3q' wawa.txt
name wawa
age 6
name feizi
# 还可以设置退出状态码
[root@MiWiFi-RA80-srv test]# sed '4q 10' wawa.txt
name wawa
age 6
name feizi
age 30
[root@MiWiFi-RA80-srv test]# echo $?
10标签命令 (
:
)::label
:创建一个标签。- 例如:
sed ':label' filename
:创建一个名为label
的标签。
跳转命令 (
b
):b label
:跳转到指定标签处继续执行。- 例如:
sed '2b label' filename
:如果第 2 行匹配,则跳转到名为label
的标签处继续执行。
这些是 sed
常用的编辑命令之外的一些其他命令。通过组合这些命令,你可以实现更复杂的文本处理操作。
除了前面提到的 sed
常用编辑命令之外,还有以下一些编辑命令:
转换命令 (
y
):y/chars1/chars2/
:将文本中chars1
中的字符替换为chars2
中对应位置的字符。- 例如:
sed 'y/abc/123/' filename
:将文件中所有的a
替换为1
,b
替换为2
,c
替换为3
。
命令执行命令 (
e
):/pattern/e command
:在匹配到的行上执行指定的 shell 命令。- 例如:
sed '/pattern/e echo "Match found"' filename
:在匹配到pattern
的行上执行echo "Match found"
命令。
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 在2,3,4,5行后附加whoami的输出内容
[root@MiWiFi-RA80-srv test]# sed '2,5e whoami' wawa.txt
name wawa
root
age 6
root
name feizi
root
age 30
root
name beizi
age 36
name zs
age 30打印行号命令 (
=
):=
:打印当前行号。将行号及其内容写入标准输出流- 例如:
sed '=' filename
:打印文件中每行的行号。 sed '$ ='filename
打印文件最后一行行号,也就是文件总共多少行
&命令
SED 支持特殊字符 &。每当模式匹配成功时,此特殊字符就会存储匹配的模式。它经常与替换命令一起使用。
1 | [root@MiWiFi-RA80-srv test]# cat a |
打印下一行命令 (
N
):N
:将下一行添加到模式空间中以供处理。- 例如:
sed 'N;s/foo\nbar/barfoo/' filename
:将匹配到foo
和bar
位于不同行的两行合并为一行,并将其替换为barfoo
。
默认情况下,SED 在单行上运行,但它也可以在多行上运行。多行命令用大写字母表示。例如,与 n 命令不同,N 命令不会清除并打印模式空间。相反,它会在当前模式空间的末尾添加换行符 (\n),并将输入文件中的下一行附加到当前模式空间,并通过执行其余的 SED 命令继续 SED 的标准流程。
n命令
n 命令打印模式缓冲区的内容,清除模式缓冲区,将下一行提取到模式缓冲区中,然后对其应用命令。
1 | [root@MiWiFi-RA80-srv test]# cat a |
- 打印模式空间命令 (
p
):/pattern/,+1p
:打印匹配到的行及其下一行。- 例如:
sed -n '/pattern/,+1p' filename
:打印包含pattern
的行及其下一行。
- 读写文本命令 (
r
、w
):**r filename
:在模式空间中的行后面追加指定文件中的内容。w filename
:将模式空间中的内容写入到指定文件中。- 例如:
sed '/pattern/r file.txt' filename
:在匹配到pattern
的行后追加file.txt
文件中的内容。
- 打印模式空间第一行(P)
与 p 命令类似, P 命令来打印由 N 命令创建的多行模式空间的第一部分(直到嵌入换行符)。如果没有N,它和p命令相同。
D
D
命令会删除模式空间中的第一行,并重新开始处理剩余的模式空间。如果剩余的模式空间不为空,D
命令会跳转到脚本的开头重新执行,直到模式空间为空为止。这种行为允许
sed
脚本在处理多行文本时实现循环处理的效果,从而允许对整个文件进行逐行处理而不是一次性处理整个文件。1
2sed '$!N; /^\(.*\)\n\1$/!P; D' filename
# 删除连续重复行检查版本(v)
SED 还提供了一个 v 命令来检查版本。如果提供的版本高于安装的 SED 版本,则命令执行失败。请注意,此选项是 GNU 特定的,可能不适用于 SED 的其他变体。
h命令
h 命令处理保持缓冲区。它将数据从模式缓冲区复制到保持缓冲区。保持缓冲区中的现有数据将被覆盖。请注意,h 命令不会移动数据,它只会复制数据。因此,复制的数据在模式缓冲器中保持原样。
1 | [~/test]: cat a |
- H命令
SED 提供了 H 命令,该命令通过在末尾添加新行将内容附加到保持缓冲区。 h 和 H 命令之间的唯一区别是,前者覆盖保持缓冲区中的数据,而后者将数据追加到保持缓冲区。
1 | [~/test]: sed -n '/Bob/!h;/Bob/{H;x;p}' a |
- g命令
将数据从保持缓冲区复制到模式缓冲区。复制时,模式空间中的现有数据会被覆盖。
1 | [~/test]: sed -n '/Bob/!h;/Bob/{p;g;p}' a |
- G命令
将保持缓冲区的内容附加到模式缓冲区。 SED 提供 G 命令,通过在末尾添加新行将内容附加到模式缓冲区。
1 | [~/test]: cat a |
x命令
交换模式空间和保持空间的内容
以上是 sed
中常用的编辑命令及其说明。通过组合这些命令,你可以实现对文本的各种操作。
与许多其他 GNU/Linux 实用程序一样,SED 也支持正则表达式,通常称为正则表达式。本章详细介绍了正则表达式。本章分为三个部分:标准正则表达式、正则表达式的 POSIX 类和元字符。
7.3.5 sed 高级用法
sed
是一个强大的文本处理工具,除了基本的替换、删除、插入等操作外,还有一些高级用法可以实现更复杂的文本处理任务。以下是一些 sed
的高级用法:
使用正则表达式:
sed
支持正则表达式,可以在编辑命令中使用正则表达式来匹配和处理文本。这使得sed
在文本处理中变得更加灵活和强大。多重编辑命令:
可以在同一条命令中使用多个编辑命令,通过分号或换行来分隔不同的编辑命令,实现一次对文本进行多个操作。例如:
1
sed -e 's/foo/bar/' -e 's/123/456/' filename
使用变量:
可以在sed
命令中使用变量来实现动态的文本处理操作。通常可以使用单引号或双引号将sed
命令包裹起来,然后在其中使用变量。例如:
1
2
3pattern="foo"
replacement="bar"
sed "s/$pattern/$replacement/g" filename递归处理目录下的多个文件:
使用find
命令结合sed
,可以递归地处理目录下的多个文件。例如:
1
2
3
4
5
6
7
8
9
10
11
12find . -type f -name "*.txt" -exec sed -i 's/foo/bar/g' {} +
在 `find` 命令中,`-exec` 选项后面可以跟随一个命令来执行。`{}` 表示 `find` 命令找到的文件名列表。`;` 表示命令的结尾,每找到一个文件就执行一次命令。`+` 则表示将找到的文件名作为参数传递给一个单独的命令,而不是每个文件都执行一次命令。
使用 `+` 可以有效地减少 `sed` 命令的调用次数,提高效率,因为 `sed` 命令一次可以处理多个文件。相比之下,使用 `;` 每次只能处理一个文件,可能会导致 `sed` 命令被调用多次,性能较低。
如果你使用 `\` 替代 `+`,即 `{} \;`,则相当于告诉 `find` 命令每次找到一个文件就执行一次 `sed` 命令。这种方式的效率较低,因为 `sed` 命令会被频繁地调用,不如使用 `+` 效率高。
# 如果不递归查找,只查找1级目录,则用-maxdepty选项
find . -maxdepth 1 -type f -name "*.txt" -exec sed -i 's/foo/bar/g' {} +条件匹配和处理:
可以使用条件语句来匹配和处理文本中的特定内容。例如,结合正则表达式和条件语句,可以根据不同的条件对文本进行不同的处理。例如:
1
sed '/pattern1/ s/foo/bar/; /pattern2/ s/baz/qux/' filename
使用分组和反向引用:
结合正则表达式的分组和反向引用,可以实现更复杂的文本处理操作,例如提取文本中的特定部分或进行模式匹配替换。例如:
1
sed 's/\(pattern1\)\(pattern2\)/\2\1/' filename
以上是 sed
的一些高级用法,结合这些技巧可以实现各种复杂的文本处理任务。
除了之前提到的一些高级用法外,还有一些其他的 sed
高级用法,包括:
地址范围匹配:
sed
支持通过地址来限定命令的作用范围。地址可以是行号、正则表达式或者地址范围。- 指定单行:
sed '3d' filename
删除第三行。 - 指定行范围:
sed '2,5d' filename
删除第二行到第五行。 - 使用正则表达式:
sed '/pattern1/,/pattern2/d' filename
删除匹配pattern1
到pattern2
之间的所有行。
- 指定单行:
命令执行顺序控制:
可以通过花括号{}
来定义命令块,并通过;
分隔不同的命令,以控制命令的执行顺序。例如:
1
2
3
4sed '/pattern/{
s/foo/bar/
s/baz/qux/
}' filename自定义标签和跳转:
可以使用标签来标记命令块,并使用b
命令来跳转到指定标签处执行命令。如果省略标签名称,则 SED 跳转到 SED 文件的末尾。例如:
1
2
3
4sed ':label /pattern/{
s/foo/bar/
b label
}' filename1
2
3
4
5
6
7
8
9
10
11
12
13
14
15root~/test: cat wawa.txt
name wawa
age 6
name feizi
age 30
name beizi
age 36
name zs
age 30
root~/test: sed 'N;s/\n/---/;/wawa/!b la;s/^/(animal)/;:la' wawa.txt
(animal)name wawa---age 6
name feizi---age 30
name beizi---age 36
name zs---age 30t命令可以重新跳到标签处
扩展正则表达式:
sed
支持-E
选项来启用扩展正则表达式,这样可以使用更多的正则表达式语法。例如:
1
sed -E 's/(pattern1|pattern2)/replacement/' filename
删除空行:
可以使用sed '/^$/d'
命令来删除空行。逆序打印:
使用tac
命令可以逆序打印文件内容,结合sed
可以实现逆序打印指定范围的行。例如:
1
2tac filename | sed -n '5,10p' | tac
sed -n '5,10p' filename |tac按字段操作:
sed
可以使用正则表达式匹配和处理字段。例如,使用\b
来匹配单词边界,或者使用\s
来匹配空白字符。
以上是一些 sed
的高级用法,结合这些技巧可以实现更加复杂和灵活的文本处理任务。
当然,还有更多关于 sed
的高级用法:
反转行内容:
使用rev
命令可以反转每一行的内容,结合sed
可以实现反转文件内容。例如:
1
sed '1!G;h;$!d' filename
统计行数:
可以使用sed
命令和wc -l
命令结合来统计文件的行数。例如:
1
sed -n '$=' filename
删除非ASCII字符:
使用sed
命令和正则表达式可以删除文本中的非ASCII字符。例如:
1
sed 's/[^[:print:]]//g' filename
查找并输出行号:
使用sed
命令和=
命令可以输出匹配行的行号。例如:
1
sed -n '/pattern/=' filename
删除指定行:
可以使用sed
命令结合行号来删除指定行。例如:
1
sed -i '5d' filename
多行操作:
通过N
命令可以将下一行添加到模式空间,然后可以对多行进行操作。例如:
1
sed -n 'N;s/foo\nbar/baz/g;p' filename
转换大小写:
使用sed
命令可以轻松转换文本中的大小写。例如:
1
sed 's/foo/bar/Ig' filename # 不区分大小写替换
在匹配行前后插入内容:
可以使用sed
命令在匹配行的前后插入内容。例如:
1
2
3
4
5sed '/pattern/{i\
Inserted text before pattern
a\
Inserted text after pattern
}' filename
这些是 sed
的一些进阶用法,能够满足更多复杂的文本处理需求。结合这些技巧,你可以更加灵活地使用 sed
来处理文本数据。
- 还有一些转义字符\d \o \x 分别代表十进制 八进制 十六进制
还有一些POSIX类和元字符
7.3.6 sed实例
1 | [root@192 test]# cat a.txt |
1 | [root@MiWiFi-RA80-srv test]# cat b.txt |
1 | [root@MiWiFi-RA80-srv test]# cat b.txt |
1 | ``` |
8. 高级命令和工具
8.1 awk
和 cut
命令
了解 awk
的详细内容将涵盖其语法、内置变量、操作符、函数、控制结构等方面。
8.1.1. awk
语法
awk
命令的基本语法结构。
awk
的基本语法结构由模式-动作组成。下面是 awk
基本语法的概述:
1 | awk 'pattern { action }' filename |
pattern
是一个模式,用于匹配输入数据中的行。{ action }
是一个动作块,当输入的行匹配模式时,将执行其中的动作。filename
是要处理的文件名。如果不指定文件名,则默认从标准输入(stdin)读取数据。
pattern
和 { action }
都可以省略,但至少要有一个。如果省略了 pattern
,则会应用于输入中的每一行。如果省略了 { action }
,则默认执行 { print }
,即打印匹配到的行。
下面是一些示例 awk
命令的用法:
- 打印文件中的所有行:
1 | awk '{ print }' filename |
- 打印文件中包含 “pattern” 的行:
1 | awk '/pattern/ { print }' filename |
- 在模式和动作之间使用多个动作,以分号分隔:
1 | awk '/pattern/ { print $1, $2; sum += $3 } END { print "Total:", sum }' filename |
这些示例展示了 awk
基本语法的使用,可以根据需要自定义模式和动作来处理文本数据。
1 | # 示例1:打印文件中的所有行 |
- 模式-动作语句的组织方式。
在 awk
中,模式-动作语句是一种用于指定在匹配到特定模式时执行的动作的结构。以下是模式-动作语句的组织方式:
1 | awk 'pattern1 { action1 } pattern2 { action2 } ... patternN { actionN }' filename |
- 每个模式-动作语句由一个模式和一个动作组成,它们被大括号
{}
包围。 - 模式用于匹配输入的行,如果匹配成功,则执行与之关联的动作。
- 动作是在匹配到模式时执行的操作,可以是任意合法的
awk
语句或命令序列。
在模式-动作语句中,可以有一个或多个模式-动作对,它们按顺序应用于输入的每一行。如果省略了模式,则相应的动作将应用于每一行。
以下是一个示例,演示了模式-动作语句的组织方式:
1 | # 如果第一列的值大于10,则打印该行 |
在这些示例中,每个模式-动作语句都包含一个模式和一个动作。模式用于选择要操作的行,而动作则指定在匹配到模式时要执行的操作。
- 使用单引号或双引号来编写
awk
程序。
在 awk
中,可以使用单引号或双引号来编写程序。这两种引号的选择主要取决于需要在程序中包含的变量和特殊字符。
使用单引号 '
1 | awk 'pattern { action }' filename |
- 单引号中的内容被视为一个整体字符串,不会对其中的变量进行扩展或替换。
- 这意味着在单引号中无法直接引用Shell变量或进行变量替换。
- 使用单引号时,变量应当在外部传入,而不是在
awk
程序中定义。
例:
1 | awk '$1 > 10 { print }' filename |
在这个示例中,单引号内的程序以字符串形式传递给 awk
,其中的 $1
和 print
作为 awk
的语法被解释,而不会被Shell进行变量替换。
使用双引号 "
1 | awk "pattern { action }" filename |
- 双引号中的内容会被Shell解释器进行扩展和替换。
- 这意味着可以在双引号中引用Shell变量,并进行变量替换。
- 使用双引号时,应注意转义字符的处理,以避免对特殊字符的意外解释。
例:
1 | awk "END { print \"Total lines: \", NR }" filename |
在这个示例中,双引号内的程序中的 NR
是一个 awk
内置变量,但是双引号使得Shell将其扩展为行数,然后再传递给 awk
。由于 "
和 \
在双引号内有特殊含义,因此需要进行转义以确保它们被 awk
正确解释。
8.1.2. 内置变量
awk
内置变量的介绍,如NF
、NR
、FS
、RS
等。
在 AWK 中,有一些内置变量可以在程序中使用。这些变量提供了对输入、输出、行号等信息的访问。以下是一些常用的 AWK 内置变量:
- **
$0
**:当前记录的内容(整行)。 - **
$1
,$2
, …,$NF
**:当前记录的第 1、2、…、最后一个字段。 - **
NF
**:当前记录中字段的数量。 - **
NR
**:已读取的记录数(行号)。 - **
FNR
**:当前文件中已读取的记录数(行号)。
1 | NR:表示当前正在处理的记录数(即行数),它从AWK开始执行以来逐行递增,即使处理多个文件,NR也会逐行增加,它不会重置。 |
- **
FILENAME
**:当前输入文件的名称。 - **
FS
**:字段分隔符(默认为空格或制表符)。 - **
RS
**:记录分隔符(默认为换行符)。 - **
OFS
**:输出字段分隔符(默认为空格)。 - **
ORS
**:输出记录分隔符(默认为换行符)。
这些内置变量可以在 awk
程序中直接使用,用于访问和处理输入数据。例如:
1 | # 打印文件的第一行 |
通过使用这些内置变量,可以编写出更加灵活和强大的 awk
程序,用于处理各种文本数据。
还有一些内置变量及其含义:
- **
ARGC
**:命令行参数的数量。 - **
ARGV
**:一个数组,包含命令行参数的内容。 - **
ENVIRON
**:一个数组,包含环境变量的内容。 - **
OFMT
**:数字的输出格式(默认为 “%.6g”)。 - **
RLENGTH
**:由match
函数设置的匹配到的子串的长度。 - **
SUBSEP
**:多维数组的分隔符(默认为 “\034”)。
这些内置变量提供了对输入、输出、环境以及处理过程的访问和控制。使用这些变量,你可以更加灵活地处理文本数据,并实现各种复杂的文本处理任务。
- 了解这些变量的含义和用法,以及它们在
awk
程序中的作用。
1 | 以下是 AWK 中常用的内置变量及其含义、用法和作用: |
8.1.3. 操作符
awk
中支持的各种操作符,包括赋值操作符、算术操作符、比较操作符和逻辑操作符。
awk
中支持多种操作符,用于对数据进行比较、计算和逻辑操作。以下是 awk
中支持的各种操作符:
算术操作符
- **
+
**:加法。 - **
-
**:减法。 - **
*
**:乘法。 - **
/
**:除法。 - **
%
**:取模(取余)。
关系操作符
- **
==
**:等于。 - **
!=
**:不等于。 - **
>
**:大于。 - **
<
**:小于。 - **
>=
**:大于等于。 - **
<=
**:小于等于。
赋值操作符
- **
=
**:赋值。 - **
+=
**:加法赋值。 - **
-=
**:减法赋值。 - **
*=
**:乘法赋值。 - **
/=
**:除法赋值。 - **
%=
**:取模赋值。
逻辑操作符
- **
&&
**:逻辑与。 - **
||
**:逻辑或。 - **
!
**:逻辑非。
拼接操作符
- **
**:空格,用于拼接字符串。
- **
$0
**:整行拼接,将所有字段连接成一个字符串。
正则表达式操作符
- **
~
**:匹配正则表达式。 - **
!~
**:不匹配正则表达式。
字段操作符
- **
$1
**:第一个字段。 - **
$2
**:第二个字段。 - **
$NF
**:最后一个字段。
其他操作符
- **
?:
**:条件表达式。 - **
,
**:逗号操作符,用于分隔表达式。
这些操作符可以组合使用,用于对数据进行各种比较、计算和逻辑操作,从而实现复杂的文本处理任务。
- 了解这些操作符的优先级和结合性,以及如何在
awk
程序中使用它们。
1 | 在 `awk` 中,操作符的优先级和结合性决定了表达式中各个操作符的计算顺序。以下是 `awk` 中常见操作符的优先级和结合性: |
- awk中也可以使用重定向 > 或 >>
它将数据附加到输出文件中。如果输出文件不存在,则会创建一个。使用这种类型的重定向时,新内容将附加在文件末尾。例如,以下示例附加 Hello, World !!!到文件。
1 | print DATA > output-file |
31.awk中也可以使用管道 |或双向通信 |&
可以通过管道而不是使用文件将输出发送到另一个程序。此重定向打开一个命令管道,并通过该管道将项目的值写入另一个进程以执行该命令。重定向参数命令实际上是一个 AWK 表达式。
1 | awk 'BEGIN { print "hello, world !!!" | "tr [a-z] [A-Z]" }' # HELLO, WORLD !!! |
8.1.4. 函数
awk
中内置的一些常用函数,如字符串函数、数学函数等。
awk
内置了许多函数,这些函数可以用于对文本数据进行各种操作,包括字符串处理、数值计算、数组操作等。以下是一些常用的 awk
内置函数:
字符串函数:
**
length(s)
**:返回字符串s
的长度。**
substr(s, m, n)
**:返回字符串s
中从位置m
开始的长度为n
的子串。如果没指定n则从m开始到结束1
2
3
4bz🤪 awk 'BEGIN{print substr("loveyou",1,3)}'
lov
bz🤪 awk 'BEGIN{print substr("loveyou",1+3)}'
eyou**
index(s1, s2)
**:返回字符串s1
中第一次出现字符串s2
的位置(从1开始),如果未找到则返回0。**
tolower(s)
**:将字符串s
转换为小写形式。**
toupper(s)
**:将字符串s
转换为大写形式。
1
2
3
4
5bz🤪 cat a.txt
i love you
i like you
server78
server871
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23bz🤪 awk '{print length($0), $0}' a.txt
10 i love you
10 i like you
8 server78
8 server87
bz🤪 awk '{print substr($0,3,3)}' a.txt
lov
lik
rve
rve
bz🤪 awk '{print index($0,"e")}' a.txt
6
6
2
2
bz🤪 awk '{print toupper($0)}' a.txt
I LOVE YOU
I LIKE YOU
SERVER78
SERVER87除了我之前提到的字符串处理函数外,AWK 还提供了其他一些常用的字符串函数,以下是其中一些:
匹配函数:
match(string, pattern, array)
sub(pattern, replacement, string)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
这个函数用于在字符串 `string` 中查找匹配 `pattern` 的子字符串,并将结果存储在数组 `array` 中。如果未提供数组参数,则返回匹配的起始位置。`pattern` 是正则表达式。
- `match(str, regexp)`:在字符串 `str` 中查找正则表达式 `regexp` 的第一个匹配项,并返回匹配的起始位置。如果找到匹配项,则返回其起始位置;否则返回 0。
`RSTART` 和 `RLENGTH` 是 AWK 中的两个特殊变量,用于存储 `match()` 函数的匹配结果。
- `RSTART`:表示最近一次 `match()` 函数匹配的子字符串在目标字符串中的起始位置(索引从 1 开始)。如果没有匹配成功,则 `RSTART` 的值为 0。
- `RLENGTH`:表示最近一次 `match()` 函数匹配的子字符串的长度。如果没有匹配成功,则 `RLENGTH` 的值为 0。
这两个变量通常与 `match()` 函数一起使用,以获取匹配子字符串的位置和长度信息。在之前提到的第二种方法中,我们使用了 `RSTART` 和 `RLENGTH` 来确定每次匹配到的数字的位置和长度,并通过 `substr()` 函数来提取这些数字,然后将其累加到总和中。
- `sub(regexp, replacement, target)`:在字符串 `target` 中查找正则表达式 `regexp` 的第一个匹配项,并用 `replacement` 替换。返回替换次数(0 或 1)。
- `gsub(regexp, replacement, target)`:在字符串 `target` 中查找所有匹配正则表达式 `regexp` 的项,并用 `replacement` 替换。返回替换次数。
Awk中sub()实例
**sub()函数**用于在字符串中进行**一次**替换操作。它只替换第一个匹配到的模式,并且不会继续搜索或替换其他匹配项。
**语法格式:**awk '{sub(" ", "_", $0); print}' file.txt1
2
3
4
5
6
7
8
9
10
11
**参数说明:**
- `pattern`: 要匹配的模式,可以是正则表达式。
- `replacement`: 替换字符串。
- `string`: 要进行替换操作的字符串。
**示例:**
1. 将字符串中的第一个空格替换为下划线:awk '{sub("[0-9]", "*", $0); print}' file.txt1
2
3
1. 将字符串中的所有数字替换为星号:awk '{sub("abc", "xyz", $0); print}' file.txt1
2
3
1. 将字符串中的第一个"abc"替换为"xyz":awk '{sub(/abc/i, "xyz", $0); print}' file.txt1
2
3
1. 将字符串中的所有"abc"替换为"xyz",并忽略大小写:awk '{gsub(" ", "", $0); print}' file.txt1
2
3
4
5
**更多示例:**
- 将字符串中的所有空格替换为空字符串:awk '{gsub("[0-9]", "0", $0); print}' file.txt1
2
3
- 将字符串中的所有数字替换为"0":awk '{gsub(/[a-z]/, toupper($0), $0); print}' file.txt1
2
3
- 将字符串中的所有字母替换为大写:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
**注意:**
- sub()函数只替换第一个匹配到的模式,如果要替换所有匹配项,可以使用**gsub()函数**。
- sub()函数和gsub()函数都可以使用正则表达式进行模式匹配。
2. **格式化函数**:
- `sprintf(format, expr1, expr2, ...)`:根据指定的格式 `format` 返回格式化后的字符串。类似于 C 语言中的 `sprintf` 函数。
3. **查找函数**:
- `index(str, search)`:在字符串 `str` 中查找子字符串 `search` 的第一个匹配项,并返回其起始位置。如果找到匹配项,则返回其起始位置;否则返回 0。
- `tolower(str)`、`toupper(str)`:分别将字符串 `str` 转换为小写或大写。
4. **拼接函数**:
- `concat(str1, str2, ...)`:将多个字符串 `str1`、`str2` 等拼接成一个字符串。
5. **分割函数**:
- `split(str, array, separator)`:将字符串 `str` 按照分隔符 `separator` 进行拆分,并将拆分后的子字符串保存到数组 `array` 中。返回拆分后的子字符串个数。
6. **去除空白函数**:
- `sub()`、`gsub()` 也可以用于去除字符串中的空白字符,例如 `sub(/^ +| +$/, "", str)` 可以去除字符串 `str` 前后的空格。
这些字符串函数可以帮助您在 AWK 中进行更复杂的文本处理任务,例如查找匹配项、替换子字符串、格式化输出等。
2. **数值函数**:
- **`sqrt(x)`**:返回 `x` 的平方根。
- **`int(x)`**:返回 `x` 的整数部分。
- **`rand()`**:返回0到1之间的随机数。
- **`srand([x])`**:设置随机数种子。如果不提供参数 `x`,则使用系统时间作为种子。
3. **数组函数**:
- **`delete array[index]`**:删除数组 `array` 中指定索引的元素。
- **`delete array`**:删除数组 `array` 的所有元素。
- **`length(array)`**:返回数组 `array` 的长度。
- **`split(str, arr, [sep])`**:将字符串 `str` 按分隔符 `sep` 分割成数组 `arr` 的元素。
Awk 中的 `split()` 函数用于将字符串分割成数组。它接受三个参数:
- **str**:要分割的字符串。
- **array**:存储分割结果的数组。
- **sep**:分割符。
`split()` 函数会将 `str` 中的每个子字符串(由 `sep` 分隔)存储在 `array` 数组中。数组的下标从 1 开始,第一个元素是分割符前的字符串,后续元素是分割符后的字符串。
```bash
bz🤪 awk 'BEGIN{str="i love you,you,love me";split (str,words," ")
for (i=1; i<=length(words); i++)
print words[i]
}'
i
love
you,you,love
me
bz🤪 awk 'BEGIN{str="i love you,you,love me";split (str,words,"[ ,]")
for (i=1; i<=length(words); i++)
print words[i]
}'
i
love
you
you
love
me
bz🤪 awk 'BEGIN{str="this is test: 1 2 3"
split(str, words," ")
for (i=1;i<=length(words);i++)
if (match(words[i], /[0-9]+/)) print words[i]
}'
1
2
3
bz🤪 awk 'BEGIN {
str = "/path/to/file.txt"
split(str,array, "/")
print array[length(array)]
}'
file.txt
日期函数:
systime()
:返回当前时间的秒数(从 1970 年 1 月 1 日至今的秒数)。strftime(format, timestamp)
:将时间戳timestamp
格式化为指定的日期/时间格式
输出函数:
- **
print items
**:输出指定的项,以空格分隔。 - **
printf format, items
**:按照指定的格式输出指定的项。
- **
文件函数:
- **
getline
**:从输入中读取下一行。 - **
getline var
**:从输入中读取下一行,并将其保存到变量var
中。
1
2
3
4
5
6
7
8
9
10bz🤪 cat b.txt
name age
aa 000
bb 999
cc 888
ac 222
bz🤪awk '{getline a; print a}'t b.txt
aa 000
cc 888
cc 888- **
close(filename)
**:关闭文件。
- **
这些函数提供了丰富的功能,可以用于处理各种文本数据和执行各种计算。通过灵活运用这些内置函数,可以编写出功能强大的 awk
程序来满足不同的需求。
- 自定义函数的创建和使用方法,以及如何在
awk
程序中调用它们。
Awk中自定义函数
Awk允许用户定义自定义函数,以便将代码进行重用,提高代码的可读性和可维护性。
Awk
1 | function function_name(parameter1, parameter2, ...) { |
参数说明:
- function_name: 函数名称,由字母、数字、下划线组成,不能以数字开头。
- parameter1, parameter2, …: 函数参数,可以是多个。
- 函数体: 函数的代码块,包含要执行的语句。
示例:
定义一个函数,用于计算两个数的平方和:
Awk
1 | function square_sum(a, b) { |
示例输入:
1 | 1 2 |
示例输出:
1 | 5 |
调用自定义函数:
在awk程序中,可以使用函数名称和参数来调用自定义函数。
示例:
Awk
1 | function print_message(message) { |
示例输出:
1 | Hello, world! |
自定义函数的优势:
- 提高代码的重用性,避免重复编写代码。
- 提高代码的可读性和可维护性,使代码更加清晰易懂。
- 可以将复杂的操作封装成函数,方便调用。
注意事项:
- 函数名称不能与awk内置函数同名。
- 函数参数必须在函数体中使用。
- 函数的返回值可以使用return语句返回。
以下是一些使用自定义函数的示例:
- 计算文件的行数:
Awk
1 | function count_lines(filename) { |
- 将字符串中的所有空格替换为下划线:
Awk
1 | function replace_spaces(str) { |
- 将字符串中的所有数字转换为大写:
Awk
1 | function to_upper(str) { |
8.1.5. 控制结构
awk
中支持的各种控制结构,包括条件语句和循环语句。- 了解如何在
awk
程序中使用if-else
语句、while
循环、for
循环等。
8.1.6. 输入处理
- 如何使用
awk
处理输入数据,包括文件输入和管道输入。 - 使用
awk
内置变量和函数来处理输入数据,如getline
函数和ARGV
数组。
1 | # 计算文字中所有数字之和 |
8.1.7. 输出格式化
1 | awk转义序列: |
awk
中如何格式化输出结果,包括字段分隔符、输出宽度和对齐方式等。- 使用
printf
函数进行复杂输出格式控制。
8.1.8. 高级功能
awk
中的高级功能,如数组的使用、多维数组、递归函数等。- 如何利用这些高级功能解决复杂的文本处理问题。
8.1.9. 实例演示
- 使用实际示例演示
awk
的使用,包括日志分析、数据转换、报告生成等常见任务。 - 通过实例帮助理解
awk
的实际应用场景和解决方案。
8.1.10. 调试和优化
awk
程序的调试技巧,如打印调试信息、使用trace
功能等。- 如何优化
awk
程序,提高性能和效率。
通过深入了解 awk
的语法和功能,你将能够更加熟练地使用它来处理各种文本数据,并解决实际的问题。
8.2 find
和 xargs
命令
8.3 sort
和 uniq
命令
9. 脚本调试和错误处理
- 9.1 使用
set
调试脚本 - 9.2 错误处理和退出状态码
- 9.3 使用
trap
处理信号
1 | 当然,我会对每种方法进行详细解析: |
10. Bash 高级特性
- 10.1 Shell 参数扩展
- 10.2 数学运算
- 10.3 特殊变量(
$?
、$$
等)
Bash(GNU Bourne-Again SHell)是一个广泛使用的Unix shell和命令语言,具有许多高级特性,使其成为一种功能强大且灵活的工具。以下是一些Bash的高级特性:
通配符扩展(Wildcard Expansion):Bash允许使用通配符(如
*
、?
等)来匹配文件名和路径名,这使得对文件进行批量操作变得更加方便。命令替换(Command Substitution):使用反引号(
)或
$()
语法,可以将命令的输出作为变量或参数传递给其他命令。变量替换(Parameter Expansion):Bash支持各种变量替换操作,如
${var}
、${var:-default}
、${var:=value}
等,用于对变量进行操作和转换。算术扩展(Arithmetic Expansion):通过双括号(
(( ... ))
)或$(( ... ))
,可以进行整数运算,并将结果赋给变量。数组(Arrays):Bash支持一维数组,可以方便地存储和操作多个值。
关联数组(Associative Arrays):自Bash 4.0开始,还支持关联数组,允许使用字符串作为索引。
条件判断(Conditional Constructs):Bash提供了丰富的条件判断结构,如
if
语句、case
语句、[[ ... ]]
条件判断等,使得编写复杂的逻辑更加简洁。循环结构(Loop Constructs):Bash支持
for
、while
、until
等循环结构,可用于重复执行代码块。函数(Functions):可以在Bash中定义和调用函数,使代码模块化和重用更加方便。
作业控制(Job Control):Bash允许在后台运行命令,并可通过
bg
、fg
等命令控制作业的状态。信号处理(Signal Handling):Bash可以捕获和处理各种信号,如
SIGINT
、SIGTERM
等,以及自定义信号。文件描述符操作(File Descriptor Manipulation):可以在Bash中打开、关闭、重定向文件描述符,实现输入输出的灵活控制。
进程替换(Process Substitution):通过
<(command)
或>(command)
,可以在不创建临时文件的情况下将命令的输出作为文件进行处理。模式匹配(Pattern Matching):Bash支持在字符串操作中使用模式匹配,如字符串替换、匹配等。
这些高级特性使得Bash成为一种强大的脚本语言和交互式shell,可用于各种系统管理、自动化任务和编程工作。
当然,还有更多的高级特性,以下是一些其他的Bash高级特性:
参数扩展(Parameter Expansion):除了变量替换,Bash还支持参数扩展,如
${parameter:offset:length}
、${parameter/pattern/string}
等,用于对参数进行操作和转换。引用(Quoting):Bash提供了多种引号机制,如单引号、双引号和反斜杠,用于对特殊字符进行转义或保留字面意义。
Here Documents:通过
<<
和结束标记,可以在脚本中嵌入多行文本块。Here Strings:通过
<<<
,可以将字符串作为命令的输入。函数参数处理:Bash函数支持位置参数,以及
$@
和$*
用于获取所有参数,$#
用于获取参数数量。进程控制:Bash支持对后台进程和作业进行控制,如
jobs
、kill
等命令,以及trap
用于捕获和处理信号。调试功能:Bash提供了调试脚本的功能,如
set -x
启用跟踪模式、set -e
在出现错误时退出等。环境变量处理:Bash可以在脚本中设置和读取环境变量,以及通过
export
命令将变量导出给子进程。输入/输出重定向:Bash支持将命令的输入和输出重定向到文件、管道或其他文件描述符。
进程控制:通过
&
将命令置于后台执行,以及nohup
、disown
等命令用于处理后台进程。Shell脚本编程风格:Bash脚本编程有许多最佳实践和约定,如代码缩进、注释、错误处理等,以提高脚本的可读性和可维护性。
这些高级特性使得Bash成为一种功能强大、灵活且易于使用的工具,适用于各种系统管理、自动化任务和编程工作。
第三部分:实际应用
11. 实际应用示例
11.1 系统管理任务
11.1.1 用户管理简单脚本
1 |
|
- 11.2 日志处理
- 11.3 自动化任务
12. 脚本最佳实践
- 12.1 代码风格和可读性
- 12.2 脚本优化和性能
- 12.3 安全性和防御性编程
13. 实用工具和框架
- 13.1 使用
jq
处理 JSON 数据 - 13.2 使用
curl
进行网络通信 - 13.3 使用第三方库和工具
14. 远程脚本执行和管理
- 14.1 使用 SSH 远程执行脚本
- 14.2 使用 Ansible 等工具进行配置管理
15. 实战项目
- 15.1 构建一个自动备份脚本
- 15.2 简单的系统监控工具