Linux扩展——shell编程

前置:Linux基础及命令复习

目录

  • shell概述
    • Shell脚本入门案例 sh bash ./ . source
  • 变量
    • 系统预定义变量 $HOME $PWD $SHELL等
    • 自定义变量 unset readonly
      • 补充:开启子Shell进程的常见方法 (...) $(...) `...` <(...) >(...)
    • 特殊变量 $n $# $* $@ $?
  • 运算符 $((表达式)) $[表达式]
  • 条件判断 test condition 或 [ condition ]
  • ★流程控制
    • if判断
    • case语句
    • for循环
    • while循环
  • read读取控制台输入
  • 函数
    • 系统函数 basename dirname
    • 自定义函数
  • ★Shell工具
    • cut
    • awk
    • sort
    • sed
    • wc
  • 正则表达式入门

shell概述

在这里插入图片描述

什么是 Shell?
Shell 是一种命令行解释器,是用户与操作系统之间交互的桥梁。它接收用户输入的命令,解释并将其传递给操作系统内核执行,然后将执行结果返回给用户。

简单来说,Shell 是一个解释器,可以理解用户的命令,也可以运行脚本实现自动化任务。

Shell 是 Linux 的核心工具之一,它不仅是一个命令行界面,还提供强大的脚本编程能力。通过学习 Shell 编程,你可以自动化许多重复的任务,提高工作效率。在大数据开发中,Shell 常被用来管理集群、自动化运行任务、处理数据等。

Shell 的主要功能

命令解释:接收并执行用户输入的命令。
脚本运行:通过编写脚本(多个命令的集合)实现自动化操作。
编程能力:支持变量、条件判断、循环、函数等编程语法。
任务调度:可以通过 cron 等工具定时运行脚本。

Linux 提供的 Shell 解析器

Bash (Bourne Again Shell):是 Linux 默认的 Shell,大多数 Linux 发行版默认安装。扩展了传统 Bourne Shell 的功能,支持命令补全、历史记录等。
Sh (Bourne Shell):一个简单而经典的 Shell,是早期 Unix 系统的默认 Shell。
功能较少,主要用于兼容性和轻量化任务。
Zsh (Z Shell):功能强大且易于定制,特别受开发者和高级用户欢迎。提供更灵活的命令补全、高亮显示和自动化功能。
Ksh (Korn Shell):综合了 C Shell 和 Bourne Shell 的优点,功能丰富。
Csh (C Shell) 和 Tcsh:类似 C 语言的语法,适合习惯 C 语言的用户。
Dash:轻量级的 Shell,适合系统脚本执行,速度快,占用资源少。

我们最常用的就是sh和bash了

[root@localhost root01]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/bin/tcsh
/bin/csh

bash和sh的关系

Sh 是一种经典的 Shell,它是传统 Unix 系统的标准 Shell。
Bash 是 GNU 开发的增强版 Shell,兼容 Sh 的功能,并扩展了许多强大的特性,比如:命令历史记录 (history)。Tab 自动补全。算术运算支持 ($((expression)))。更丰富的脚本功能和控制结构。
兼容性:绝大部分情况下,Bash 可以运行 Sh 脚本,但反之不一定成立。

在centos7下观察发现,sh其实就是bash的软链接:

[root@localhost bin]# ll | grep bash
-rwxr-xr-x.   1 root root       964536 41 2020 bash
lrwxrwxrwx.   1 root root           10 31 2024 bashbug -> bashbug-64
-rwxr-xr-x.   1 root root         6964 41 2020 bashbug-64
lrwxrwxrwx.   1 root root            4 31 2024 sh -> bash

# Centos默认的解析器是bash
[root@localhost bin]# echo $SHELL
/bin/bash

Shell脚本入门案例 sh bash ./ . source

需求 : 创建一个Shell脚本,输出helloworld

[root01@localhost test]$ touch helloworld.sh
[root01@localhost test]$ vim helloworld.sh 

在helloworld.sh中输入如下内容

#!/bin/bash
echo "helloworld"

#!/bin/bash 这一行被称为 shebang(也叫做 hashbang)。它的作用是指定脚本文件应当使用的解释器。

脚本的常用执行方式

第一种:采用bashsh+ 脚本的相对路径绝对路径(不用赋予脚本+x权限)

​ sh+脚本的相对路径/绝对路径

[root01@localhost test]$ sh helloworld.sh 
helloworld
[root01@localhost test]$ sh /home/root01/test/helloworld.sh
helloworld

​ bash+脚本的相对路径/绝对路径

[root01@localhost test]$ bash helloworld.sh 
helloworld
[root01@localhost test]$ /home/root01/test/helloworld.sh
helloworld

第二种:采用输入脚本的绝对路径或相对路径执行脚本(必须具有可执行权限+x)

上面的第一种执行方法,本质是bash解析器帮你执行脚本,所以脚本本身不需要执行权限。第二种执行方法,本质是脚本需要自己执行,所以需要执行权限。

[root01@localhost test]$ ./helloworld.sh
bash: ./helloworld.sh: 权限不够
# 赋予helloworld.sh 脚本的+x权限
[root01@localhost test]$ chmod +x helloworld.sh
 # 相对路径
[root01@localhost test]$ ./helloworld.sh 
helloworld
# 绝对路径
[root01@localhost test]$ /home/root01/test/helloworld.sh 
helloworld 

第三种:在脚本的路径前加上“.”或者 source

假如有以下脚本

[root01@localhost test]$ cat test.sh 
#!/bin/bash
A=5
echo $A

分别使用shbash./. 的方式来执行,结果如下:

[root01@localhost test]$ bash test.sh 
5
[root01@localhost test]$ echo $A

[root01@localhost test]$ sh test.sh 
5
[root01@localhost test]$ echo $A

[root01@localhost test]$ chmod +x test.sh 
[root01@localhost test]$ ./test.sh 
5
[root01@localhost test]$ echo $A

[root01@localhost test]$ . test.sh 
5
[root01@localhost test]$ echo $A
5
[root01@localhost test]$ source test.sh 
5
[root01@localhost test]$ echo $A
5
[root01@localhost test]$ 

为什么shbash./的方式,echo $A输出没反应呢?
前三种方式都是在当前shell中打开一个子shell来执行脚本内容,当脚本内容结束,则子shell关闭,回到父shell中。这意味着:
1.脚本中的变量和环境修改:仅在子 shell 中有效,不会影响当前的父 shell。
2.执行结束后:子 shell 关闭,所有在脚本中设置的变量在父 shell 中不可见。
第四种,也就是使用在脚本路径前加“.”或者 source的方式,可以使脚本内容在当前shell里执行,而无需打开子shell!这意味着:
1.变量和环境修改:直接作用于当前 shell,脚本中设置的变量在执行后依然存在。
2.无需启动子 shell:所有命令在当前环境中执行,修改当前环境的变量和设置。

这也是为什么我们每次要修改完/etc/profile文件以后,需要source一下的原因。
开子shell与不开子shell的区别就在于,环境变量的继承关系,如在子shell中设置的当前变量,父shell是不可见的。


什么是 source 命令?

source 是一个 shell 内建命令,用于在当前 shell 环境中读取和执行指定的脚本文件。它通常用于加载配置文件或在当前环境中运行脚本以便修改环境变量。

source 常用来做什么?
1.加载配置文件:当你修改了像 ~/.bashrc、~/.bash_profile 或 /etc/profile 这样的配置文件后,需要重新加载它们以使修改生效,而无需重新启动终端。可以使用 source. 命令来实现。
2.执行需要修改当前 shell 环境的脚本:如果脚本中包含环境变量的设置、别名的定义或其他需要在当前 shell 中生效的操作,应使用 source 执行。

为什么修改 /etc/profile这样的系统级的配置文件后需要 source?
/etc/profile 是一个系统级的配置文件,当用户登录时,系统会自动读取并执行该文件中的内容,以设置环境变量、别名等。然而,修改 /etc/profile 后,这些修改只会在新的 shell 会话中生效。如果你希望在当前会话中立即应用这些更改,而不必重新登录或启动一个新的终端,可以使用 source 命令来重新加载配置文件。


变量


系统预定义变量 $HOME $PWD $SHELL等

以下是一些常用的系统变量及其作用说明:

$HOME:当前用户的主目录。例如,/home/username。
$PWD:当前工作目录的完整路径。
$SHELL:用户当前使用的默认Shell类型及其路径。例如,/bin/bash。
$USER:当前登录的用户名。
$HOSTNAME:当前主机名。
$PATH:可执行文件的搜索路径,多个路径之间用冒号分隔。
$UID:当前用户的用户ID。
$EUID:当前用户的有效用户ID(尤其在切换用户权限时有区别)。
$?:上一条命令的退出状态码,0表示成功,非0表示失败。
$$:当前Shell脚本的进程ID。

系统变量的调用不区分大小写,但按照习惯,系统变量通常使用大写字母

案例实操

# 使用 echo 命令可以直接查看某个系统变量的值。例如,查看当前用户的主目录:
[root@localhost root01]# echo $HOME
/root

# 例如当前工作目录:
[root@localhost root01]# echo $PWD
/home/root01

# 使用 set 命令可以查看当前Shell中所有的变量
# 包括系统预定义变量和用户自定义变量。
# 执行命令后,会输出大量信息
[root@localhost root01]# set
BASH=/bin/bash
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
...

# 通过查看 $SHELL 变量,可以知道当前使用的默认Shell类型
[root@localhost root01]# echo $SHELL
/bin/bash
# 如果你想确认当前Shell是否与默认Shell一致,可以查看 $0 变量,它表示当前运行的Shell或脚本名称:
[root@localhost root01]# echo $0
bash

# 通过 $? 可以获取上一条命令的退出状态码。
[root@localhost root01]# ls /nonexistentpath
ls: 无法访问/nonexistentpath: 没有那个文件或目录
[root@localhost root01]# echo $?
2 # 返回的状态码 2 表示命令执行失败

# 使用 $$ 可以获取当前脚本的进程ID,这在调试或日志记录中非常有用
[root@localhost root01]# echo $$
105311

# 如果要查看变量的完整清单,除了 set 命令外
# 还可以使用 env 命令(仅显示环境变量)
[root@localhost root01]# env
XDG_VTNR=1
XDG_SESSION_ID=2
...

# 使用 export 命令可以将用户自定义变量转为环境变量,供子进程使用
[root@localhost root01]# MY_VAR="hello"
[root@localhost root01]# export MY_VAR
[root@localhost root01]# env | grep MY_VAR
MY_VAR=hello


自定义变量 unset readonly

1.基本语法

定义变量:变量名=变量值注意=号前后不能有空格

Shell 的赋值语法是 VAR=value,而 value 必须是一个静态字符串或通过命令替换捕获(后面会介绍)的结果,不能直接是直接调用命令本身。

撤销变量:unset 变量名

声明静态变量:readonly变量,注意:不能unset。在当前Shell进程中会一直存在,除非当前Shell会话结束

2.变量定义规则

变量名规则:变量名称只能包含字母、数字和下划线,不能以数字开头。按约定俗成,环境变量名一般使用大写,普通变量名使用小写。

语法规则:等号=两侧不能有空格,否则会导致语法错误。如果变量值包含空格或特殊字符,必须使用引号(单引号或双引号)括起来。

默认类型:在bash中,所有变量的默认类型为字符串类型,无法直接进行数值运算。

作用域:普通变量的作用域仅限于当前Shell进程。通过 export 命令可以将变量提升为全局环境变量,供子Shell进程使用。

3.案例实操

# (1)定义变量A并查看其值
[root01@localhost test]$ A=5
[root01@localhost test]$ echo $A
5
# (2)给变量A重新赋值
[root01@localhost test]$ A=8
[root01@localhost test]$ echo $A
8
# (3)撤销变量A
[root01@localhost test]$ unset A
[root01@localhost test]$ echo $A # 执行 unset 后,变量A被撤销,再次输出时为空。
# (4)声明静态变量B
[root01@localhost test]$ readonly B=2
[root01@localhost test]$ echo $B
2
[root01@localhost test]$ B=9
bash: B: 只读变量 # 尝试修改静态变量时,系统会报错提示其为只读变量。
# (5)默认字符串类型的数值运算
[root01@localhost test]$ C=1+2
[root01@localhost test]$ echo $C
1+2 # 在Shell中,变量值为字符串,不能直接进行算术运算

# 如果需要运算,可使用 expr 或 $(( ))。
[root01@localhost test]$ C=$((1+2))
[root01@localhost test]$ echo $C
3
# 或者 C=$(expr 1 + 2)
# expr 对空格敏感,运算符和操作数之间必须有空格,否则会报错
# 注意:在 expr 中,乘法符号 * 必须用反斜杠 \ 转义,否则会被Shell解释为通配符。
# $(( )) 对空格不敏感,可以省略空格。$(( )) 更简洁,更适合现代Shell脚本的书写

# (6)处理带空格的变量值
[root01@localhost test]$ D=I love banzhang
bash: love: 未找到命令... # 未使用引号时,变量赋值会因空格导致语法错误
# 正确写法是使用双引号或单引号
[root01@localhost test]$ D="I love banzhang"
[root01@localhost test]$ echo $D
I love banzhang
# (7)将变量提升为全局环境变量
# 通过 export 命令,可以将变量变为全局环境变量,供子Shell进程访问
# 创建脚本 helloworld.sh 并添加如下内容:
#!/bin/bash
echo "helloworld"
echo $B
# 执行脚本前未导出变量B时,脚本无法访问变量B的值
[root01@localhost test]$ ./helloworld.sh 
helloworld

# 导出变量后,再次执行脚本
[root01@localhost test]$ export B
[root01@localhost test]$ ./helloworld.sh 
helloworld
2


补充:开启子Shell进程的常见方法 (…) $(…) `…` <(…) >(…)

上面说到:普通变量的作用域仅限于当前Shell进程。通过 export 命令可以将变量提升为全局环境变量,供子Shell进程使用。

那么什么是子Shell进程; 一般开启子Shell进程的方法有哪些?

子Shell进程简介
子Shell进程是从当前Shell进程派生出的一个新的Shell实例。子Shell是当前Shell的子进程,它可以继承当前Shell的环境变量,但不会影响当前Shell的变量。当子Shell进程终止时,其对环境变量的修改不会反作用于父Shell进程。
在Linux系统中,每一个进程都有一个父进程,Shell本身也是一个进程(例如,bash Shell是由系统启动的)。当Shell启动一个新的子进程时,这个新进程称为子Shell。

另外,下文章节会介绍函数,在 Shell 脚本中,不能直接将函数调用赋值给变量。为了捕获函数的输出,必须使用命令替换 $(…) 或反引号 `…`。

这部分可以等看完函数这一章节再看

开启子Shell进程的常见方法

1.运行脚本
当我们运行一个Shell脚本时,会自动启动一个子Shell来执行脚本中的命令。

# 创建一个脚本 test.sh
#!/bin/bash
echo "This is a child shell"
echo "Current PID: $$"

# 运行脚本:
bash test.sh
# 输出:
# This is a child shell
# Current PID: 12345  # 子Shell的进程ID

$$ 是Shell的特殊变量,用于显示当前Shell进程的ID,前面系统变量时有提到。


2.使用子Shell语法 ()
在一对小括号 () 中运行的命令会启动一个子Shell。
作用:隔离作用域,子Shell的变量、环境和操作不会影响父Shell。
是否可用于获取函数结果:不能直接将函数结果赋值给变量。

echo "Parent Shell PID: $$"
(
  echo "This is a child shell"
  echo "Child Shell PID: $$"
)
# 输出:
# Parent Shell PID: 6789
# This is a child shell
# Child Shell PID: 12345

# 脚本里也是一样。内容(假设文件名为 test.sh):
#!/bin/bash
echo "Parent Shell PID: $$"
(
  echo "This is a child shell"
  echo "Child Shell PID: $$"
)
# 运行脚本:
bash test.sh
Parent Shell PID: 12345
This is a child shell
Child Shell PID: 67890

子Shell的PID与父Shell的PID不同。


3.通过管道 |

使用管道操作符时,每一端的命令通常会在子Shell中运行。

echo "hello world" | grep "hello"

在这个例子中,echogrep 会分别运行在不同的子Shell中。


4.在命令替换中启动子Shell

当使用命令替换$(…) 或反引号`…`时,也会启动子Shell。
含义:执行括号内的命令(或函数)并捕获其标准输出,将结果作为值返回。
作用:常用于将函数或命令的输出赋值给变量。
是否可用于获取函数结果:可以,这是获取函数输出的推荐方法。
注意:$(…) 是现代的命令替换语法,推荐使用它而不是反引号(因为它在嵌套时需要转义,容易引起混淆)

result=$(echo "This is a child shell")
echo $result
# 命令 echo "This is a child shell" 是在子Shell中执行的

result=`echo "This is a child shell"`
echo $result
# 反引号 `...` 是另一种实现命令替换的方式,与 $(...) 功能相同
# 都会启动一个子Shell来执行命令,并将其输出返回到父Shell。

# 虽然反引号和 $() 都可以实现命令替换,但 $() 是更现代和推荐的写法,原因如下:
# 1.$() 可以更清晰地嵌套命令,而反引号嵌套时需要转义,很容易出错
# 2.$() 更直观,特别是对于复杂的命令链,易于阅读和维护。反引号在代码中容易与其他字符混淆,特别是在某些字体中。

5.通过子Shell启动新的Shell会话

可以手动启动一个子Shell(例如执行 bashsh):

[root01@localhost test]$ echo $$
65202
[root01@localhost test]$ bash # 进入子Shell
[root01@localhost test]$ echo $$ # 查看子Shell的PID
65248
[root01@localhost test]$ exit # 退出子Shell,回到父Shell
exit
[root01@localhost test]$ echo $$
65202

6.使用重定向符号 <()>()

Process Substitution 是一种特殊的情况,它允许在子Shell中运行命令,然后将其输出作为文件进行处理。

cat <(echo "This is a child shell")
# 在这个例子中,echo "This is a child shell" 是在子Shell中执行的。

<(…)
含义:将命令的输出作为一个临时文件的路径(FIFO或临时文件),供其他命令读取。
作用:通常用于需要将命令输出作为文件输入的场景。
是否可用于获取函数结果:间接可以,但更适合传递文件形式的输出,不推荐直接用于变量赋值。

>(…)
含义:将命令的输出写入临时文件路径供其他命令使用。
作用:通常用于需要将命令的输出重定向到文件的场景。
是否可用于获取函数结果:不能直接用于变量赋值。


特殊变量 $n $# $* $@ $?

在Shell脚本中,有许多特殊变量可以用来获取脚本信息、参数信息以及命令执行的状态。熟练掌握这些变量有助于编写更灵活和高效的脚本。

总结:
$n 用于获取具体位置的参数。
$# 获取参数总数。
$*$@ 用于获取所有参数,区别在于处理方式。
$? 用于检查命令或脚本执行的状态。

下面分别介绍

1 $n

$n是位置参数变量:
$0 表示当前脚本的名称。
$1$9 表示传递给脚本的第1到第9个参数。
超过9个参数时,需要使用大括号,如 ${10}

案例实操。创建脚本 parameter.sh:

#!/bin/bash

echo '========== $n =========='
echo $0   # 输出脚本名
echo $1   # 输出第一个参数
echo $2   # 输出第二个参数

执行脚本:

[root01@localhost test]$ chmod 777 parameter.sh 
[root01@localhost test]$ ./parameter.sh ab cd ef
========== $n ==========
./parameter.sh
ab
cd

2 $#

$# 表示传递给脚本的参数总数,常用于循环或参数验证。

案例实操。扩展 parameter.sh:

#!/bin/bash

echo '========== $n =========='
echo $0   # 脚本名
echo $1   # 第一个参数
echo $2   # 第二个参数

echo '========== $# =========='
echo $#   # 参数总数

执行脚本:

[root01@localhost test]$ ./parameter.sh tx nb hah
========== $n ==========
./parameter.sh
tx
nb
========== $# ==========
3

3 $* 和 $@

$* 将所有的参数视为一个整体,参数间用空格隔开。
$@ 将每个参数独立处理,参数间仍用空格隔开。

案例实操。更新 parameter.sh:

#!/bin/bash

echo '========== $n =========='
echo $0   # 脚本名
echo $1   # 第一个参数
echo $2   # 第二个参数

echo '========== $# =========='
echo $#   # 参数总数

echo '========== $* =========='
echo $*   # 所有参数作为一个整体

echo '========== $@ =========='
echo $@   # 每个参数独立输出

执行脚本:

[root01@localhost test]$ ./parameter.sh tx nb hah 1 2 3 4
========== $n ==========
./parameter.sh
tx
nb
========== $# ==========
7
========== $* ==========
tx nb hah 1 2 3 4
========== $@ ==========
tx nb hah 1 2 3 4
[root01@localhost test]$ 

区别补充:在大多数情况下,$*$@ 表现一致。
在双引号中使用时,行为不同:$* 将所有参数作为单个字符串(参数用空格隔开)。$@将每个参数保留为独立的字符串。示例:

#!/bin/bash
echo "Using \"\$*\":"
for arg in "$*"; do
    echo $arg
done

echo "Using \"\$@\":"
for arg in "$@"; do
    echo $arg
done

执行结果:

[root01@localhost test]$ ./parameter.sh a b c

Using "$*":
a b c

Using "$@":
a
b
c

4 $?

$? 表示最后一次执行命令的返回状态:返回 0 表示成功。
返回非 0 表示失败(错误码由命令本身决定)。

案例实操。验证脚本执行是否成功:

[root01@localhost test]$ ./helloworld.sh
hello world

[root01@localhost test]$ echo $?
0

验证错误命令:

[root01@localhost test]$ invalid_command
-bash: invalid_command: command not found

[root01@localhost test]$ echo $?
127

扩展:使用 $? 检查执行结果并给出提示

#!/bin/bash

./helloworld.sh
if [ $? -eq 0 ]; then
    echo "Script executed successfully!"
else
    echo "Script execution failed!"
fi


运算符 $((表达式)) $[表达式]

在 Shell 脚本中,可以使用以下两种方式进行数学运算:

$((表达式)):推荐的写法,支持更多功能,易读性更高。
$[表达式]:较旧的写法,功能较为简单,但在现代 Shell 中仍可使用。

[root01@localhost test]$ S=$[(2+3)*4]
[root01@localhost test]$ echo $S
20
[root01@localhost test]$ S=$(( (2+3)*4 ))
[root01@localhost test]$ echo $S
20

更多运算符支持

Shell 中支持以下运算符,适用于 $(( ))$[ ]

算术运算符+-*/%(取余)。
关系运算符<><=>===!=(返回布尔值)。
逻辑运算符&&||

例如:

[root01@localhost test]$ X=$((5 > 3 && 2 < 4))
[root01@localhost test]$ echo $X
1  # 1表示条件成立 跟执行命令的返回状态不同(0代表执行成功)

注意事项

整数运算:Shell 原生支持的运算仅限于整数。如果需要处理小数,可借助 bc 工具。例如:

[root01@localhost test]$ echo "scale=2; 10/3" | bc
3.33

空格敏感性:运算式中的空格可能会导致错误。例如:

[root01@localhost test]$ S=$[ (2 + 3) * 4 ]  # 正确
[root01@localhost test]$ S=$[ (2+3)*4 ]      # 正确
[root01@localhost test]$ S=$[ ( 2+3 )*4 ]    # 错误

兼容性问题:某些较旧的 Shell 可能对 $(( )) 的支持不完善,需确认 Shell 版本。



条件判断 test condition 或 [ condition ]

1) 基本语法

条件判断在 Shell 脚本中非常重要,用于根据条件执行不同的逻辑操作。主要有以下两种形式:

test condition

[ condition ] (注意 condition 前后需要有空格)

注意 : 条件"非空即为 true"。例如:

[ hello ] 返回 true。

[ ] 返回 false。

2) 常用判断条件

(1) 整数之间的比较

-eq : 等于(equal)

-ne : 不等于(not equal)

-lt : 小于(less than)

-le : 小于等于(less equal)

-gt : 大于(greater than)

-ge : 大于等于(greater equal)

(2)按照文件权限进行判断

-r:文件是否有读权限(read)

-w:文件是否有写权限(write)

-x:文件是否有执行权限(execute)

(3)按照文件类型进行判断

-e:文件是否存在(existence)

-f:文件是否存在且是一个常规文件(file)

-d:文件是否存在且是一个目录(directory)

(4)复杂条件判断

-a:表示逻辑与(and)。

-o:表示逻辑或(or)。

在[ condition ] 或 test 中可用,但在复杂条件中建议使用 && 和 || 代替
比如: if [ condition1 -a condition2 ] 换成 if [ condition1 ] && [ condition2 ]

3) 案例实操

(1)判断 23 是否大于等于 22

[root01@localhost test]$ [ 23 -ge 22 ]
[root01@localhost test]$ echo $?
0 # 条件 [ 23 -ge 22 ] 为真,返回值 0 表示判断成功。

(2)判断 helloworld.sh 是否具有写权限

[root01@localhost test]$ [ -w helloworld.sh ]
[root01@localhost test]$ echo $?
0 # 如果文件 helloworld.sh 存在并具有写权限,返回值为 0 表示判断成功。

(3)判断 /home/test/cls.txt 文件是否存在

[root01@localhost test]$ [ -e /home/test/cls.txt ]
[root01@localhost test]$ echo $?
1 # 条件 [ -e /home/test/cls.txt ] 为假,返回值 非0 表示判断失败

(4)多条件判断

使用 &&:前一个命令执行成功时,才会执行后一个命令。

使用 ||:前一个命令执行失败时,才会执行后一个命令。

# 案例 1:判断非空字符串
[root01@localhost test]$ [ haha ] && echo OK || echo notOK
OK # [ atguigu ] 条件为真,执行 echo OK

# 案例 2:判断空字符串
[root01@localhost test]$ [ ] && echo OK || echo notOK
notOK # [ ] 条件为假,执行 echo notOK

(5)if 语句配合条件判断(下节重点介绍)

条件判断通常与 if 语句结合使用,以下是基本格式:

if [ condition ]; then
    # 条件为真时执行的语句
else
    # 条件为假时执行的语句
fi

比如:

[root01@localhost test]$ if [ -e helloworld.sh ]; then
> echo "File exists"
> else
> echo "File does not exist"
> fi


★流程控制


if判断

基本语法

单分支:

if [ 条件判断式 ]; then
    # 程序
fi

# 或者
if [ 条件判断式 ]
then
    # 程序
fi

多分支:

if [ 条件判断式 ]; then
    # 程序
elif [ 条件判断式 ]; then
    # 程序
else
    # 程序
fi

注意:
1.[ condition ] 还可以换成test condition,具体见上一节的条件判断介绍
2.[ 条件判断式 ] 中,必须确保中括号和条件判断式之间有空格。if 关键字后面也要有空格。

案例实操:输入一个数字,如果是1,则输出 banzhang zhen shuai;如果是2,则输出 cls zhen mei;其他情况下,不输出任何内容。

编写脚本 if.sh:

#!/bin/bash

if [ $1 -eq 1 ]; then
    echo "banzhang zhen shuai"
elif [ $1 -eq 2 ]; then
    echo "cls zhen mei"
fi

运行脚本:

chmod 777 if.sh
./if.sh 1
# 输出banzhang zhen shuai


case语句

基本语法

case $变量名 in
"值1")
    # 如果变量的值等于值1,则执行程序1
    ;;
"值2")
    # 如果变量的值等于值2,则执行程序2
    ;;
*)
    # 如果变量的值不匹配上述任何一个值,则执行此程序
    ;;
esac

注意事项:
case 行尾必须是关键字 in。
每个模式匹配后需要以右括号 ) 结束。
双分号 ;; 表示命令序列结束,相当于 Java 的 break。
最后的 *) 是默认匹配模式,相当于 Java 的 default。

案例实操 :输入一个数字,如果是1,则输出 banzhang;如果是2,则输出 cls;否则,输出 renyao。

编写脚本 case.sh:

#!/bin/bash

case $1 in
"1")
    echo "banzhang"
    ;;
"2")
    echo "cls"
    ;;
*)
    echo "renyao"
    ;;
esac

运行脚本:

chmod 777 case.sh
./case.sh 1
# banzhang


for循环

基本语法

# 基本语法 1
for (( 初始值; 循环条件; 变量变化 ))
do
    # 程序
done

# 基本语法 2
for 变量 in 值1 值2 值3 …
do
    # 程序
done

案例实操1 : 计算从1加到100的和。

编写脚本 for1.sh:

#!/bin/bash

sum=0
for ((i=1; i<=100; i++)); do
    sum=$((sum + i))
done
echo $sum

运行脚本:

chmod 777 for1.sh
./for1.sh
# 5050

案例实操2: 打印所有输入参数

编写脚本 for2.sh:

#!/bin/bash

# 检查是否有输入参数
if [ $# -eq 0 ]; then
    echo "No parameters were provided."
    exit 1
fi

# 打印总参数数量
echo "Total number of parameters: $#"

# 按序号逐个打印参数
echo "Parameters:"
i=1
for param in "$@"; do
    echo "Parameter $i: $param"
    i=$((i + 1))
done

# 打印所有参数的拼接形式
echo "All parameters as a single string (\$*): $*"
echo "All parameters individually quoted (\$@): $@"

# 处理双引号包含的 $* 和 $@
echo "All parameters as a single string in quotes (\"\$*\"): \"$*\""
echo "All parameters individually quoted (\"\$@\" with quotes):"
for param in "$@"; do
    echo "\"$param\""
done

运行脚本:

chmod 777 print_params.sh
./print_params.sh cls mly wls
#输出:
Total number of parameters: 3
Parameters:
Parameter 1: cls
Parameter 2: mly
Parameter 3: wls
All parameters as a single string ($*): cls mly wls
All parameters individually quoted ($@): cls mly wls
All parameters as a single string in quotes ("$*"): "cls mly wls"
All parameters individually quoted ("$@" with quotes):
"cls"
"mly"
"wls"

$*$@ 的行为在被双引号包含时有所不同。具体见特殊变量部分介绍。



while循环

基本语法

while [ 条件判断式 ]
do
    # 程序
done

案例实操:计算从1加到100的和

编写脚本 while.sh:

#!/bin/bash

sum=0
i=1
while [ $i -le 100 ]; do
    sum=$((sum + i))
    i=$((i + 1))
done
echo $sum

运行脚本:

chmod 777 while.sh
./while.sh
# 输出:5050


read读取控制台输入

基本语法

read (选项) (参数)

常用选项:
-p:指定提示符,提示用户输入内容;
-t:指定读取输入的等待时间(单位:秒)。若超时未输入,命令将返回一个空值。
-s:隐藏用户输入内容,适用于输入密码等场景

参数:指定接收用户输入的变量名。

实用案例: 提示用户在 7 秒内输入姓名

创建脚本read.sh:

#!/bin/bash

# 提示用户在7秒内输入姓名
read -t 7 -p "Enter your name in 7 seconds: " NAME

# 输出用户输入的姓名
echo "Your name is: $NAME"

执行脚本

[root01@localhost test]$ ./read.sh
Enter your name in 7 seconds: hah
Your name is: hah

# 如果用户未在 7 秒内输入任何内容,$NAME 变量将为空
Your name is: 

扩展知识

1.read 命令可以一次读取多个值,并将它们按顺序存储到指定的变量中:

#!/bin/bash

# 提示用户输入多个值
read -p "Enter your first name and last name: " FIRSTNAME LASTNAME

# 输出结果
echo "First Name: $FIRSTNAME"
echo "Last Name: $LASTNAME"
Enter your first name and last name: John Doe
First Name: John
Last Name: Doe

2.读取多行:read 默认读取一行内容,如果需要多行输入,可结合 while 循环使用:

#!/bin/bash

echo "Enter multiple lines (Ctrl+D to finish):"

# 使用 while 循环读取多行
while read LINE; do
    echo "You entered: $LINE"
done

3.结合条件语句,可以为未输入值的变量提供默认值:

#!/bin/bash

# 提示用户输入
read -t 5 -p "Enter your favorite color: " COLOR

# 如果用户未输入,赋予默认值
COLOR=${COLOR:-"Blue"}

echo "Your favorite color is: $COLOR"
# 超时或直接按下回车:
Enter your favorite color: 
Your favorite color is: Blue

4.变量作用域:使用 read 读取的变量默认是全局变量,若在函数中使用,需配合 local 关键字限制其作用域

Shell 脚本中定义的变量默认是全局变量,无论是在函数内部还是外部定义。
如果在函数中声明变量时使用了 local 关键字,则该变量的作用域仅限于函数内部。函数外无法访问该变量:local 变量名=值



函数

系统函数 basename dirname

dirnamebasename 是一对常用的命令,分别负责提取路径的目录部分和文件名部分。结合 dirnamebasename,可以方便地拆分路径为目录和文件名。

一、basename

basename命令用于从路径中提取文件名。它会移除所有路径前缀(包括最后一个 ‘/’),并返回文件名部分。如果指定了后缀 suffix,则从文件名中去除指定的后缀。

基本语法 basename [string / pathname] [suffix]

选项说明:
string / pathname:输入的字符串或路径。
suffix(可选):指定后缀,若匹配则会从文件名中去除。

案例实操:

# 截取路径中的文件名称
[root01@localhost test]$ basename /home/test/banzhang.txt
banzhang.txt

# 截取路径中的文件名称,并去除后缀
[root01@localhost test]$ basename /home/test/banzhang.txt .txt
banzhang

二、dirname

dirname命令用于提取文件路径中的目录部分。它会移除路径中非目录的部分(文件名),返回剩余的路径部分。

案例实操:

# 获取文件的路径部分
[root01@localhost test]$ dirname /home/test/banzhang.txt
/home/test

常用的其他系统函数:
以下是一些类似于 dirname 和 basename 的常用工具命令:

realpath:解析路径,返回绝对路径
readlink:获取符号链接的目标路径
find:在目录中查找文件或目录
cut:按字段或字符分割字符串
awksed:强大的文本处理工具,后面单独介绍



自定义函数

在 Shell 脚本中可以定义自定义函数,封装一段可以复用的脚本逻辑。函数必须先声明后调用。

基本语法

[ function ] funname[()] {
    # 1. 函数体,执行操作
    Action;
    # 2. 返回值
    [return int;]  # 只返回整数作为状态码
    # 或
    echo "string_result"  # 返回字符串结果
}

各部分含义:
[ function ]:可选的关键字 function 用于声明函数。在 POSIX 标准下,函数的声明可以不需要 function 关键字,直接写 funname() 即可。如:

function myfunc() { echo "Hello"; }
# 或直接
myfunc() { echo "Hello"; }

funname[()]:funname 是函数名,自定义。括号 () 是函数调用的标志,在 Shell 中通常可以省略内部参数定义(不像其他编程语言需要明确参数)。

在 Shell 中,确实可以认为 函数是没有显式形参的。这与Shell的设计初衷:简单和轻量级、灵活性有关。函数通过$1、$2…等隐式参数机制实现了参数的传递和处理。

注意:1.函数内的 $1、$2 等是基于函数调用时传递的参数,而不是脚本启动时的参数。参数数量不固定,$# 表示参数个数,$*$@ 表示所有参数。
2.默认情况下,函数中的变量是全局的。可以使用 local关键字声明局部变量,避免与全局变量冲突。

Action:函数体,包含执行的操作内容。可以是任意 Shell 命令或逻辑

[return int;] :可选部分,用于指定返回值。Shell 中 return 只能返回 整数值(0-255)。其实返回值不是严格的“类型”,而是进程退出码,通常用来指示函数运行的状态。0:表示成功。非零:表示错误状态或特定的返回码。

经验技巧

1.函数声明顺序:必须在调用函数之前声明函数,因为 Shell 脚本是逐行解释执行,而不是像其他语言一样先编译后执行。
2.函数返回值:函数的返回值通过 $? 系统变量获取。
可以显式使用 return n(返回值范围为 0-255)。如果未显式指定返回值,Shell 会将函数中最后一条命令的运行结果作为返回值。return仅用于返回整数,表示函数执行状态。echo可以返回字符串或复杂结果,用于调用者进一步处理:

# 函数返回整数
function check_number() {
    if [ $1 -gt 0 ]; then
        return 0  # 正数
    else
        return 1  # 非正数
    fi
}
check_number -5
echo $?  # 输出 1

# 函数返回字符串:
function get_filename() {
    echo "$(basename $1)"
}
result=$(get_filename "/home/user/file.txt")
echo $result  # 输出 file.txt

倒数第二行使用了$(...)在 Shell 脚本中,不能直接将函数调用赋值给变量,原因是 函数的输出是通过标准输出 (stdout) 提供的,而不是直接作为返回值赋值。为了捕获函数的输出,必须使用命令替换 $(…) 或反引号 `…`。

案例实操 : 计算两个输入参数的和

创建脚本文件 fun.sh:

#!/bin/bash

function sum() {
    s=0
    s=$[$1 + $2]  # 使用[]进行简单数学运算
    echo "$s"      # 输出结果
}

# 提示用户输入两个数字
read -p "Please input the number1: " n1
read -p "Please input the number2: " n2

# 调用函数计算并显示结果
# 如果未显式指定返回值,Shell 会将函数中最后一条命令的运行结果作为返回值
sum $n1 $n2

优化建议:在函数中定义局部变量,避免意外覆盖全局变量。例如:

function sum() {
    local s=0  # 声明局部变量
    s=$[$1 + $2]
    echo "$s"
}

运行:

[root01@localhost test]$ chmod 777 fun.sh
[root01@localhost test]$ ./fun.sh
Please input the number1: 2
Please input the number2: 5
7


★Shell工具


cut

cut 的主要功能是用于“剪切”数据,从文件的每一行中提取字节、字符或字段并输出,适合处理结构化文本。默认以制表符作为分隔符。

基本用法

cut [选项参数] filename

常用选项:

选项参数功能案例
-f指定列号,提取指定列cut -f 1 cut.txt 提取文件 cut.txt 的第 1 列内容
-d指定字段分隔符,默认是制表符 \tcut -d ":" -f 2 /etc/passwd 使用 : 分隔符,提取第 2 列
-c提取指定的字符cut -c 1-3 cut.txt 提取每行前 3 个字符
--complement提取除指定列或字符外的其他内容cut -d “:” --complement -f 1 /etc/passwd 排除第 1 列内容
--output-delimiter自定义输出分隔符cut -d ":" -f 1,3 --output-delimiter="-" /etc/passwd 输出使用 - 分隔

案例实操

# 数据准备
[root01@localhost test]$ touch cut.txt
[root01@localhost test]$ vim cut.txt
dong shen
guan zhen
wo wo
lai lai
le le
# 提取第一列内容(以空格为分隔符)
[root01@localhost test]$ cut -d " " -f 1 cut.txt
dong
guan
wo
lai
le

# 提取第二列和第三列(以空格为分隔符)
[root01@localhost test]$ cut -d " " -f 2,3 cut.txt
shen
zhen
wo
lai
le

# 提取每行前 3 个字符
[root01@localhost test]$ cut -c 1-3 cut.txt
don
gua
wo 
lai
le 

# 提取 PATH 变量的,以":"为分隔符的,第3个及后续路径
[root01@localhost test]$ echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/root/bin
[root01@localhost test]$ echo $PATH | cut -d ":" -f 3-
/usr/bin:/usr/sbin:/bin:/sbin:/root/bin

# 自定义输出分隔符("->"),提取/etc/passwd文件的第一和第三列(以":"为分隔符)
[root01@localhost test]$ cut -d ":" -f 1,3 --output-delimiter="->" /etc/passwd
root->0
daemon->1
...


awk

awk 是一款功能强大的文本处理工具,能够以逐行的方式处理文件内容,通过匹配模式和自定义操作实现灵活的文本处理。

基本用法

awk [选项参数] '/pattern1/{action1} /pattern2/{action2}... ' filename

pattern:表示awk在数据中查找的内容,即匹配模式(可以是正则表达式)。
action:在找到匹配内容时,执行的一系列命令(可以是打印、计算等操作)。默认情况下,如果没有指定action,则打印匹配行。

常用选项

选项参数功能案例
-F指定输入文件的字段分隔符将/etc/passwd文件按冒号分隔并打印第一列和第七列:awk -F ':' '{print $1, $7}' /etc/passwd
-v给awk脚本赋值一个外部变量定义变量var并将其与第一列一起打印 : awk -v var=5 '{print $1, var}' file
-f从文件中加载awk脚本在文件script.awk中定义awk脚本并应用于文件:awk -f script.awk file
--结束选项参数,表明后续参数将不再被视为选项,而是输入文件名或脚本内容awk -- '{print $1}' --help 如果没有 ----help 会被解析为选项,显示帮助信息

案例实操

# 数据准备
[root01@localhost test]$ sudo cp /etc/passwd ./

# 搜索passwd文件中以root开头的所有行,并输出该行的第7列
# 这里使用了-F ':'指定了字段分隔符为冒号(:),并通过正则表达式/^root/匹配以root开头的行
[root01@localhost test]$ awk -F ':' '/^root/{print $7}' passwd
/bin/bash
/bin/bash

# 搜索passwd文件中以root开头的所有行,并输出该行的第1列和第7列,列之间用逗号分隔
[root01@localhost test]$ awk -F ':' '/^root/{print $1","$7}' passwd
root,/bin/bash
root01,/bin/bash

# 显示passwd的第一列和第七列(不指定pattern),以逗号分隔
# 且在所有行前面添加列名"user,shell",在最后一行添加dahaige,/bin/zuishuai
[root01@localhost test]$ awk -F ':' 'BEGIN{print "user,shell"} {print $1","$7} END{print "dahaige,/bin/zuishuai"}' passwd
user,shell
root,/bin/bash
bin,/sbin/nologin
...
dahaige,/bin/zuishuai

# 将passwd文件中的用户ID(第三列)增加1并输出
[root01@localhost test]$ awk -v i=1 -F ':' '{print $3+i}' passwd
1
2
3
4
...

BEGIN块在开始处理文件之前执行,通常用于初始化输出或打印列名。
END块在所有行处理完成后执行,通常用于打印总结或添加额外的输出。

awk的内置变量

awk有多个内置变量,帮助我们在处理文本时更加灵活和高效。以下是常用的几个内置变量:

变量名说明示例
FILENAME当前处理的文件名每行打印当前文件名和第一列的内容:awk '{print FILENAME, $1}' file.txt
NR已读记录的行号(即当前处理到的行数)每行打印行号和整行内容 : awk '{print NR, $0}' file
NF当前记录(行)的字段数(即列数)按冒号分割每行,打印行号、列数和整行内容:awk -F ':' '{print NR, NF, $0}' /etc/passwd
FS输入字段分隔符(默认是空格或制表符)将输入字段分隔符设置为冒号后,打印第一列和第三列内容:awk 'BEGIN {FS=":"} {print $1, $3}' /etc/passwd
RS输入记录分隔符(默认是换行符)将记录分隔符设置为双换行,打印记录号和每条记录内容:awk 'BEGIN {RS="\n\n"} {print NR, $0}' file

示例详解

# FILENAME 示例 打印当前文件名和每行的第一列内容
[root01@localhost test]$ awk '{print FILENAME, $1}' passwd 
passwd root:x:0:0:root:/root:/bin/bash
passwd bin:x:1:1:bin:/bin:/sbin/nologin
...

# NR 示例 每行打印行号和整行内容。NR 表示当前行号,$0 表示整行内容
[root01@localhost test]$ awk '{print NR, $0}' /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
...

# NF 示例 按冒号分割每行,打印行号、列数和整行内容
# NF 表示当前行的字段(列)数量,NR 表示行号
[root01@localhost test]$ awk -F ':' '{print NR, NF, $0}' /etc/passwd
1 7 root:x:0:0:root:/root:/bin/bash
2 7 bin:x:1:1:bin:/bin:/sbin/nologin
...

# FS 示例 FS 指定分隔符为冒号,打印第一列(用户名)和第七列(默认登录 Shell)
[root01@localhost test]$ awk 'BEGIN {FS=":"} {print $1, $7}' /etc/passwd
root /bin/bash
bin /sbin/nologin
...

# RS 示例 RS设置输入记录分隔符为单个换行(默认值),每行被视为一条记录
# FS设置字段分隔符为冒号(:),用于分隔每行中的字段
[root01@localhost test]$ awk 'BEGIN {RS="\n"; FS=":"} {print NR, $1, $7}' /etc/passwd
1 root /bin/bash
2 bin /sbin/nologin
...
# 如果将 RS 设置为双换行符,则可以将连续的空行作为记录分隔符。例如:
awk 'BEGIN {RS="\n\n"; FS=":"} {print NR, $1, $7}' /etc/passwd
# 假设/etc/passwd 文件有多余空行,比如:
root:x:0:0:root:/root:/bin/bash

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 执行结果:
1 root /bin/bash
2 bin /sbin/nologin
3 daemon /sbin/nologin

其他常用用法

# 条件语句:awk支持类似于编程语言的条件判断,如if、else等
awk '{if ($3 > 5000) print $1, $3}' passwd

# 算术运算:awk可以进行基本的算术运算
awk '{print $1, $3*2}' passwd

# 字符串操作:awk可以操作字符串,如连接、长度等
awk '{print $1, length($1)}' passwd

awk是一个功能非常强大的文本处理工具,适用于从文本文件中提取、处理和分析数据,还有很多用法不赘述了。



sort

sort 命令在 Linux 中非常有用,用于对文本文件中的行进行排序,并将结果输出到标准输出或文件。sort 支持多种排序方式和功能,是日常文本处理和脚本编写的利器。

基本语法

sort [选项] [文件...]

常用选项:

选项说明
-n按数值大小排序(数字比较)
-r按降序(逆序)排序
-t设置排序时使用的分隔符
-k指定排序的键列(从 1 开始计数)
-u去重(删除重复的行)
-o指定输出结果的文件(原地排序)
-c检查文件是否已经排序,若未排序则报错
-f忽略大小写进行排序
-b忽略每行前面的空格进行排序
--version-sort按版本号排序(适用于软件版本号等排序)

案例实操

# 数据准备
[root01@localhost test]$ touch sort.txt
[root01@localhost test]$ vim sort.txt
# 文件内容为:
bb:40:5.4
bd:20:4.2
xz:50:2.3
cls:10:3.5
ss:30:1.6

# 按照“:`分隔符,第 3 列倒序排序。-t : 指定使用冒号 : 作为分隔符;-k 3 表示按第 3 列排序;-n 表示按数值大小排序;-r 表示按降序排序
[root01@localhost test]$ sort -t : -nrk 3 sort.txt
bb:40:5.4
bd:20:4.2
cls:10:3.5
xz:50:2.3
ss:30:1.6

# 去除重复行并按字母顺序排序。假设文件内容为:
apple
orange
banana
apple
grape
[root01@localhost test]$ sort -u sort.txt
apple
banana
grape
orange
# -u 选项会删除重复行。默认按字母顺序排序。

# 忽略大小写排序。假设文件内容为:
apple
Orange
Banana
APPLE
[root01@localhost test]$ sort -f sort.txt
APPLE
apple
Banana
Orange

# 按版本号排序。假设文件内容为:
file-1.2.1
file-1.2.10
file-1.10.5
file-1.3.3
[root01@localhost test]$ sort --version-sort sort.txt
file-1.2.1
file-1.2.10
file-1.3.3
file-1.10.5
# --version-sort 按版本号(逻辑顺序)排序,而不是按字典顺序。

# 检查文件是否已排序。假设文件内容为:
apple
banana
orange
grape
[root01@localhost test]$ sort -c sort.txt
sort:sort.txt:4:无序: grape
# 如果文件已排序,则无输出;如果未排序,则报错。

# 按多个字段排序。假设文件内容为:
Tom,20,85
Jerry,19,90
Spike,21,85
Tyke,20,95
# 先按分数(第 3 列)降序排序,如果分数相同,再按年龄(第 2 列)升序排序:
[root01@localhost test]$ sort -t , -k3,3nr -k2,2n sort.txt
Tyke,20,95
Jerry,19,90
Tom,20,85
Spike,21,85
# 命令解析:-t ,:指定 , 作为分隔符;-k3,3nr:-k3,3 表示只排序第 3 列,n 表示按数值排序,r 表示降序;-k2,2n:当第 3 列相同时,按第 2 列升序排序


sed

sed(Stream Editor)是 Linux 中一个强大的文本处理工具,用于对文本流进行处理和转换。它能够执行查找、替换、插入、删除等操作,广泛应用于批量文本编辑、日志处理和自动化脚本中。

基本语法

sed [选项] '命令' 文件...

常用选项:

选项说明
-e指定要执行的编辑命令(可以多次使用)
-i直接修改文件(原地编辑),无需输出到新文件
-n禁止自动输出,配合 p 命令显示指定行
-f从文件中读取命令(脚本文件)
-r使用扩展正则表达式(ERE)
-s合并连续的空白字符(用于空白处理)
--version显示 sed 版本信息

常用命令和案例

1. 替换(s 命令).最常用的 sed 功能是替换字符串。其基本语法为:
sed 's/原字符串/新字符串/[替换选项]' 文件
默认只替换行中的第一个匹配项.全局替换:使用 g 选项替换每行的所有匹配项.忽略大小写:使用 I 选项忽略大小写
# 示例:替换第一个出现的字符串.将文件中的 "apple" 替换为 "orange"
sed 's/apple/orange/' file.txt
# 示例:全局替换所有 "apple" 为 "orange"
sed 's/apple/orange/g' file.txt
# 示例:替换并忽略大小写.将文件中的 "apple" 和 "APPLE" 替换为 "orange"
sed 's/apple/orange/I' file.txt

2. 删除行(d 命令).sed 可以用来删除文件中的某些行。常见的删除方式有删除指定行和删除匹配某模式的行
# 示例:删除第 2 行
sed '2d' file.txt
# 示例:删除所有包含 "apple" 的行
sed '/apple/d' file.txt

3. 插入和追加(i 和 a 命令).可以使用 i 命令在指定行之前插入内容,使用 a 命令在指定行之后追加内容。
# 示例:在第 2 行之前插入文本。在第 2 行之前插入 "Hello"
sed '2i Hello' file.txt

4. 替换行(c 命令).使用 c 命令可以完全替换指定行的内容。
# 示例:替换第 2 行内容.替换第 2 行的内容为 "This is a new line"
sed '2c This is a new line' file.txt

5. 显示行(p 命令).使用 p 命令配合 -n 选项,可以仅输出匹配的行,避免默认输出所有行。
# 示例:显示匹配 "apple" 的行.仅显示包含 "apple" 的行
sed -n '/apple/p' file.txt

6. 替换并修改文件(-i 选项).使用 -i 选项可以让 sed 直接修改文件,而不需要通过重定向输出到新文件。
# 示例:直接在文件中替换内容.将文件中的所有 "apple" 替换为 "orange"(原地修改)
sed -i 's/apple/orange/g' file.txt

7. 正则表达式支持.sed 支持基本正则表达式(BRE),也可以通过 -r 选项启用扩展正则表达式(ERE),使得正则表达式功能更加丰富。
# 示例:使用扩展正则表达式进行替换.使用扩展正则表达式(-r),将所有以字母开头的单词替换为 "REPLACED"
sed -r 's/\b[a-zA-Z]+\b/REPLACED/g' file.txt

8. 批量替换多个文件.通过使用通配符,sed 可以对多个文件执行相同的替换操作。
# 示例:批量替换多个文件中的 "apple" 为 "orange"
sed -i 's/apple/orange/g' *.txt

9. 显示指定行范围(n 命令).使用 n 命令,可以显示指定行范围的内容。
# 示例:显示第 2 到第 4 行 
sed -n '2,4p' file.txt

10. 使用 -f 选项读取脚本.sed 允许将多个编辑命令保存在脚本文件中,然后通过 -f 选项调用该脚本执行。
# 示例:从脚本文件读取命令.从脚本文件 "script.sed" 中读取命令
sed -f script.sed file.txt
# script.sed 内容可能是这样的:
s/apple/orange/g
2d

11. 批量删除空行
sed '/^$/d' file.txt


wc

wc(word count)命令用于统计文件中的信息,包括行数、单词数、字符数以及字节数等,是一个非常实用的文本分析工具。

基本语法

wc [选项参数] 文件名

常用选项参数说明

选项参数功能说明示例
-l统计文件行数wc -l filename 输出文件的总行数
-w统计文件的单词数wc -w filename 输出文件的单词总数
-m统计文件的字符数wc -m filename 输出文件的字符总数
-c统计文件的字节数wc -c filename 输出文件的字节总数

案例实操

# 统计文件行数
[root01@localhost test]$ wc -l /etc/profile
78 /etc/profile # 结果表示 /etc/profile 文件有 20 行

# 统计文件的单词数
[root01@localhost test]$ wc -w /etc/profile
255 /etc/profile

# 组合选项,统计多个信息 可以组合多个选项一次性查看文件的行数、单词数和字节数:
[root01@localhost test]$ wc -lwmc /etc/profile
  78  255 1855 1855 /etc/profile

# wc 命令还可以同时统计多个文件的信息。例如:
[root01@localhost test]$ wc -lwmc file1.txt file2.txt
10 50 300 300 file1.txt
15 80 500 500 file2.txt
25 130 800 800 total

# wc 经常与其他命令组合使用,比如统计当前目录下文件的总行数
[root01@localhost test]$ ls | wc -l # 输出结果表示当前目录下的文件数量
# 或者统计日志文件中包含特定关键字的行数
grep "error" logfile | wc -l


正则表达式入门

正则表达式是一种强大的文本模式匹配工具,用于描述、查找符合某个模式的字符串。在Linux系统中,诸如grepsedawk等命令都支持正则表达式功能。正则表达式广泛用于日志分析、文本处理、数据验证等场景。

正则表达式的基本形式是通过匹配一个或多个字符来找出符合条件的字符串。简单的字符串匹配就可以使用常规表达式,不包含特殊字符时,它就匹配其自身。例如:

cat /etc/passwd | grep hah # 这条命令会匹配所有包含 atguigu 的行

常用特殊字符

1.特殊字符:^(匹配行首)。^ 表示匹配行的开头。例如:

cat /etc/passwd | grep ^a # 这条命令会匹配所有以字母 a 开头的行。

补充思考:^ 在正则表达式中是一个定位符,用于限定匹配的位置

2.特殊字符:$(匹配行尾).$ 表示匹配行的结尾。例如:

cat /etc/passwd | grep t$ # 这条命令会匹配所有以字母 t 结尾的行。

补充思考:^$ 匹配的是什么?^$ 会匹配空行,也就是说,匹配那些没有任何内容的行。

3.特殊字符:.(匹配任意单个字符). 匹配除换行符外的任何单个字符。例如:

cat /etc/passwd | grep r..t # 这条命令会匹配包含 r 和 t 之间有两个字符的行,如 rabt、root、r1xt 等。

补充:如果你需要匹配换行符,可以使用 -P(Perl兼容模式)或其他特定的工具,因为正则表达式通常不匹配换行符。

4.特殊字符:*(匹配前一个字符零次或多次)。* 与前一个字符(或表达式)一起使用,表示匹配该字符零次或多次。例如:

cat /etc/passwd | grep ro*t 
# 这条命令会匹配所有以 r 开头、以 t 结尾,并且 o 可以出现零次或多次的行,如 rt、rot、root、rooot 等。

补充思考:.* 匹配什么?.* 匹配任意字符(除了换行符)出现零次或多次。通常用于匹配任何内容

cat /etc/passwd | grep ^.*root.*$ # 这条命令会匹配所有包含 root 的行,不论 root 前后有什么字符

5.特殊字符:[](字符集匹配).[] 用于匹配括号中的任意一个字符。例如:

cat /etc/passwd | grep r[a,b,c]*t
# 这条命令会匹配包含字母 r 后跟 a、b 或 c 之一,并且之后跟随 t 的所有行,例如 rt、rat、rbt、rabt 等。

常见的字符集包括:

[0-9]:匹配一个数字字符。
[a-z]:匹配一个小写字母。
[A-Z]:匹配一个大写字母。
[a-zA-Z]:匹配一个字母。
[0-9a-fA-F]:匹配一个十六进制字符。

补充思考:cat /etc/passwd | grep r[a-c, e-f]*t 匹配什么?
这条命令匹配包含 r 后跟 a、b、c 或 e、f 字符之一,后面跟着零个或多个字符,然后以 t 结尾的所有行

6.特殊字符:\(转义字符)。\用于转义特殊字符,使它们失去特殊含义,变成字面字符。例如,若我们想匹配包含字符 $ 的行,可以使用反斜杠来转义 $

cat /etc/passwd | grep a\$b
# 这条命令会匹配所有包含 a$b 的行。

补充思考:在正则表达式中,\ 是一个非常重要的字符。许多特殊字符(如 ()*+?{}[]|等)都需要使用反斜杠来进行转义

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/941324.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络-GRE Over IPSec实验

一、概述 前情回顾&#xff1a;上次基于IPsec VPN的主模式进行了基础实验&#xff0c;但是很多高级特性没有涉及&#xff0c;如ike v2、不同传输模式、DPD检测、路由方式引入路由、野蛮模式等等&#xff0c;以后继续学习吧。 前面我们已经学习了GRE可以基于隧道口实现分支互联&…

【运维笔记】向日葵远程:输入法大写无法切换至小写

项目场景&#xff1a; 向日葵&#xff1a;远程客户电脑ubuntu系统 客户电脑&#xff1a;windows 10 &#xff0c;并安装向日葵 服务器&#xff1a;ubuntu系统 问题描述 维护ubuntu时突然无法切换成小写&#xff0c;导致无法运维 原因分析&#xff1a; 大写键被锁住 解决方案…

「Mac畅玩鸿蒙与硬件46」UI互动应用篇23 - 自定义天气预报组件

本篇将带你实现一个自定义天气预报组件。用户可以通过选择不同城市来获取相应的天气信息&#xff0c;页面会显示当前城市的天气图标、温度及天气描述。这一功能适合用于动态展示天气信息的小型应用。 关键词 UI互动应用天气预报数据绑定动态展示状态管理 一、功能说明 自定义…

AAAI-2024 | 大语言模型赋能导航决策!NavGPT:基于大模型显式推理的视觉语言导航

作者&#xff1a;Gengze Zhou, Yicong Hong, Qi Wu 单位&#xff1a;阿德莱德大学&#xff0c;澳大利亚国立大学 论文链接&#xff1a; NavGPT: Explicit Reasoning in Vision-and-Language Navigation with Large Language Models &#xff08;https://ojs.aaai.org/index.p…

react杂乱笔记(一)

程序“npx”无法运行: 找不到应用程序所在位置 行:1 字符: 1 解决方法; 不要在vscode中执行命令,在cmd 中可以执行 Module not found: Error: Cant resolve web-vitals in D:\learn\react-basic\src ERROR in ./src/reportWebVitals.js 5:4-24 Module not found: Error: Cant…

【计算机视觉】opencv-停车位检测原理及代码演示

概述 本文介绍了一种基于OpenCV库的停车场空位检测方法。通过本项目演示&#xff0c;可以对opencv库有更深刻的理解。文章详细阐述了检测原理、算法流程以及代码实现。 一、原理介绍 基于OpenCV的停车位检测原理涉及多个图像处理步骤&#xff0c;以下将结合相关公式详细介绍每…

华为认证考试模拟题测试题库(含答案解析)

你还在为华为认证数通考试的备考而烦恼吗&#xff1f; 还在纠结如何高效复习&#xff0c;掌握考点吗&#xff1f; 腾科IT教育为广大考生提供了华为认证考试模拟题库&#xff0c;让你在考试前轻松应对各种题型&#xff0c;提升做题能力与考试信心&#xff01; 【单选题】 PPP…

序列化和反序列化(一)

因为通过这段时间的学习&#xff0c;发现&#xff0c;序列化和反序列化的考点和漏洞在平时遇到的还是比较多的&#xff0c;而且自己也没有特别去学习过这个知识点&#xff0c;所以在这里写一篇关于这里序列化和反序列话的博客&#xff0c;废话就停止在这里了。 在介绍具体的序列…

优雅草央千澈-关于蓝湖如何快速的标注交互原型是如何使用的-如何使用蓝湖设计交互原型和整个软件项目的流程逻辑-实践项目详细说明

优雅草央千澈-关于蓝湖如何快速的标注交互原型是如何使用的-如何使用蓝湖设计交互原型和整个软件项目的流程逻辑-实践项目详细说明 问题背景 我们ui设计师在设计完整套ui的时候一般要标注原型&#xff0c;但是如果ui对项目整体理解不够深刻的时候&#xff0c;一般就产品经理需要…

三维测量与建模笔记 - 7.3 表面建模概念和方法

基本概念 当我们通过3D扫描设备对物体进行扫描后&#xff0c;会得到三维点云&#xff0c;通过表面建模&#xff0c;我们会重建出物体的3D模型。如果想得到完整的物体的3D模型&#xff0c;需要对物体进行多个角度的扫描并通过拼接算法重建。经过处理得到的3D模型&#xff0c;在很…

共模电感的工作原理

共模电感也称为共模扼流线圈&#xff0c;是一种抑制共模干扰的器件&#xff0c;它是由两个尺寸相同&#xff0c;匝数相同的线圈对称地绕制在同一个铁氧体环形磁芯上&#xff0c;形成的一个四端器件。当共模电流流过共模电感时&#xff0c;磁芯上的两个线圈产生的磁通相互叠加&a…

汉塔科技-上网行为管理系统 ping.php 远程命令执行漏洞复现

0x01 产品简介 汉塔科技是一家专注于网络应用软件开发的公司,在网络安全、网络协议分析以及网络数据流控制等领域拥有丰富的经验和雄厚的技术实力。其上网行为管理系统是应用识别丰富、网络安全防护强大的专业设备之一,汉塔科技上网行为管理系统是一款功能强大、应用识别丰富…

图书管理系统5,制作第十天

1. 在Java中&#xff0c;BigDecimal 类提供了多种方法可以用来将其转换为 String 类型。以下是几种常见的方法&#xff1a; 使用 toString() 方法&#xff1a; 这是将 BigDecimal 转换为 String 的最直接的方法。 Java 深色版本 BigDecimal bd new BigDecimal("123.456&…

机器视觉检测相机基础知识 | 颜色 | 光源 | 镜头 | 分辨率 / 精度 / 公差

注&#xff1a;本文为 “keyence 视觉沙龙中机器视觉检测基础知识” 文章合辑。 机器视觉检测基础知识&#xff08;一&#xff09;颜色篇 视觉检测硬件构成的基本部分包括&#xff1a;处理器、相机、镜头、光源。 其中&#xff0c;和光源相关的最重要的两个参数就是光源颜色和…

Linux shell脚本用于常见图片png、jpg、jpeg、webp、tiff格式批量转PDF文件

Linux Debian12基于ImageMagick图像处理工具编写shell脚本用于常见图片png、jpg、jpeg、webp、tiff格式批量转PDF文件&#xff0c;”多个图片分开生成多个PDF文件“或者“多个图片合并生成一个PDF文件” 在Linux系统中&#xff0c;使用ImageMagick可以图片格式转换&#xff0c…

批量提取zotero的论文构建知识库做问答的大模型(可选)——含转存PDF-分割统计PDF等

文章目录 提取zotero的PDF上传到AI平台保留文件名代码分成20个PDF视频讲解 提取zotero的PDF 右键查看目录 发现目录为 C:\Users\89735\Zotero\storage 写代码: 扫描路径‘C:\Users\89735\Zotero\storage’下面的所有PDF文件,全部复制一份汇总到"C:\Users\89735\Downl…

解决:websocket 1002 connection rejected 426upgrade required

这是问题是websocket客户端访问websocket服务端时候报的错&#xff0c;并非代码错误&#xff0c;需要配置一下k8s的路由策略 新增两行

Flutter组件————Container

Container Container 是 Flutter 中最常用的布局组件之一 参数 参数名称类型描述alignmentAlignmentGeometry定义子组件在其内部的对齐方式&#xff0c;默认为 null&#xff0c;即不改变子组件的位置。paddingEdgeInsetsGeometry内边距&#xff0c;用于在子组件周围添加空间…

ElasticSearch - 深入解析 Elasticsearch Composite Aggregation 的分页与去重机制

文章目录 Pre概述什么是 composite aggregation&#xff1f;基本结构after 参数的作用问题背景&#xff1a;传统分页的重复问题after 的设计理念响应示例 after 如何确保数据不重复核心机制Example步骤 1: 创建测试数据创建索引插入测试数据 步骤 2: 查询第一页结果查询第一页返…

MySQL 8.0:explain analyze 分析 SQL 执行过程

介绍 MySQL 8.0.16 引入一个实验特性&#xff1a;explain formattree &#xff0c;树状的输出执行过程&#xff0c;以及预估成本和预估返 回行数。在 MySQL 8.0.18 又引入了 EXPLAIN ANALYZE&#xff0c;在 formattree 基础上&#xff0c;使用时&#xff0c;会执行 SQL &#…