Linux文本处理处罚&shell脚本

[复制链接]
发表于 2026-2-9 13:12:48 | 显示全部楼层 |阅读模式
文本处理处罚

在举行文本处理处罚时,我们有一些常见的需求:


  • 获取文本的行数、字数
  • 比力两段文本的差别之处
  • 检察文本的开头几行和末了几行
  • 在文本中查找字符串
  • 在文本中更换字符串
下面先容怎样在 shell 中做到这些事故。
文本统计:wc

wc 是文本统计的常用工具,它可以输出文本的行数、单词数与字符(字节)数。
  1. $ wc file
  2.      427    2768   20131 file
复制代码
#统计中文文本时的标题
   wc 在统计中文文本时,会出现一些标题,好比:
  1. $ echo '中文测试' | wc
  2. 1       1      13
复制代码
这里表现文本只有 1 个单词,但是有 13 个字符,这显然是不对的。
  对于字符数统计结果,可以使用 wc -m 下令要求 wc 思量宽字符:
  1. $ echo '中文测试' | wc -m
  2. 5
复制代码
换行符也是一个符号,以是结果为 5(而非 4)。
  由于中文文本的单词统计涉及分词算法标题,wc 无法正确统计。
  文本比力:diff

diff 工具用于比力两个文件的差别,并列出差别。
  1. $ echo hello > file1
  2. $ echo hallo > file2
  3. $ diff file1 file1
  4. $ diff file1 file2
  5. 1c1
  6. < hello
  7. ---
  8. > hallo
复制代码
  小知识
  加参数 -w 可忽略全部空缺字符, -b 可忽略空缺字符的数目厘革。
  如果比力的是两个文本文件,差别之处会被列出;如果比力的是二进制文件,只会指出是否有差别。
  

文本开头与末端:head & tail

   顾名思义,head 和 tail 分别用来表现开头和末端指定命量的笔墨。
  以 head 为例,这里给出共同的用法:
  

  • 不加参数的时间默认表现前 10 行
  • -n <NUM> 指定行数,可简化为 -<NUM>
  • -c <NUM> 指定字节数
  1. $ head file  # 显示 file 前 10 行
  2. $ head -n 25 file  # 显示 file 前 25 行
  3. $ head -25 file  # 显示 file 前 25 行
  4. $ head -c 20 file  # 显示 file 前 20 个字符
  5. $ tail -10 file  # 显示 file 最后 10 行
复制代码
  除此以外,tail 另有一个非常实用的参数 -f:当文件末了内容增长时,连续输出末了增长的内容。这个参数常用于动态表现 log 文件的更新(试一试 tail -f /var/log/syslog)
  

文本查找:grep

   grep 下令可以查找文本中的字符串:
  1. $ grep 'hello' file  # 查找文件 file 中包含 hello 的行
  2. $ ls | grep 'file'  # 查找当前目录下文件名包含 file 的文件
  3. $ grep -i 'Systemd' file  # 查找文件 file 中包含 Systemd 的行(忽略大小写)
  4. $ grep -R 'hello' .  # 递归查找当前目录下内容包含 hello 的文件
复制代码
不止云云!
  grep 毕竟上黑白常强盛的查找工具,第九章将在先容正则表达式语法之后进一步先容 grep。
  


文本更换:sed

   sed 下令可以更换文本中的字符串:
  1. $ sed 's/hello/world/g' file  # 将文件 file 中的 hello 全局(global)替换为 world 后输出
  2. $ sed 's/hello/world/' file  # 将文件 file 的每一行第一个出现的 hello 替换为 world 后输出
  3. $ echo 'helloworld' | sed 's/hello/world/g'  # 管道也是可以的
  4. $ sed -i 's/hello/world/g' file  # -i 参数会直接写入文件,操作前记得备份哦!
  5. $ sed -i.bak 's/hello/world/g' file  # 当然,也可以让 sed 帮你备份到 file.bak
复制代码
对于大多数用户来说,最常用 sed 的场合是更换软件源的时间。在阅读了上面的示例之后,以下例子就很简单了:
  1. $ sudo sed -i 's/cn.archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
  2. $ sudo sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
  3. $ sudo sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
复制代码
同样不止云云!
  sed 毕竟上黑白常强盛的文本利用工具,不但支持正则表达式,而且可以或许做的利用也不止是更换。第九章将进一步先容 sed
    echo 'helloworld' | sed 's/hello/world/g'
  这条下令使用了 `echo` 和 `sed` 工具,在 Unix/Linux 体系中常见。
  ### 下令表明
- `echo 'helloworld'`:这个部分会输出字符串 `helloworld`。
- `|`:管道符号,用于将前一个下令的输出转达给下一个下令。
- `sed 's/hello/world/g'`:这个部分是一个 `sed` 下令,实验以下利用:
  - `s/hello/world/g` 是 `sed` 的更换下令。
    - `s` 表现更换。
    - `hello` 是要被更换的字符串。
    - `world` 是更换后的字符串。
    - `g` 表现全局更换,即全部匹配的地方都会被更换。
  ### 团体功能
整条下令的功能是将字符串 `helloworld` 中的 `hello` 更换为 `world`,因此输出结果将是:
  ```
worldworld
```
sed -i 's/hello/world/g' file  # -i 参数会直接写入文件,利用前记得备份哦!
sed -i.bak 's/hello/world/g' file  # 固然,也可以让 sed 帮你备份到 file.bak

  这两条 `sed` 下令的紧张功能是实验字符串更换利用,但它们在处理处罚文件的方式上有所差别。让我们逐条分析一下。
  ### 第一条下令
  ```bash
$ sed -i 's/hello/world/g' file  # -i 参数会直接写入文件,利用前记得备份哦!
```
  #### 具体表明
- **`sed`**:流编辑器,用于对文本举行处理处罚。
  
- **`-i`**:这个选项告诉 `sed` 直接在文件中举行修改,而不输出到标准输出(即终端)。也就是说,`sed` 会直接更换文件内的内容,而不是仅仅表现修改后的结果。
  - **`'s/hello/world/g'`**:这是 `sed` 的更换下令:
  - **`s`** 表现更换(substitute)。
  - **`hello`** 是要被更换的字符串。
  - **`world`** 是用来更换的字符串。
  - **`g`** 表现全局更换,也就是说文件中全部出现的 `hello` 都会被更换为 `world`。
  - **`file`**:这是要利用的文件名。`sed` 将会在这个文件中举行字符更换。
  #### 结果
- 文件 `file` 中的全部 `hello` 会被更换为 `world`,而原始内容将直接被覆盖,由于使用了 `-i` 选项。因此,务必在运用该下令之前备份原文件,以防错误利用导致数据丢失。
  ### 第二条下令
  ```bash
$ sed -i.bak 's/hello/world/g' file  # 固然,也可以让 sed 帮你备份到 file.bak
```
  #### 具体表明
- **`sed`**:流编辑器。
  - **`-i.bak`**:该选项与 `-i` 有些差别。这里的 `.bak` 指定了 `sed` 在直接修改文件之前,会创建一个文件备份。原始文件在修改前会被复制并生存为 `file.bak`。
  - **`'s/hello/world/g'`**:更换下令与之前的一样,举行全局字符串更换,将全部 `hello` 更换为 `world`。
  - **`file`**:同样指要利用的文件名。
  #### 结果
- 这条下令将天生一个备份文件 `file.bak`,此中生存了原始的 `file` 内容。然后,`file` 中的全部 `hello` 字符串将被更换为 `world`。
  ### 总结
- **第一条下令** 直接修改文件,而不保存任何备份。
- **第二条下令** 在修改文件之前创建了备份,以便规复大概丢失的原始内容。
  在处理处罚紧张文件时,使用第二条下令是更安全的选择,以防不测的数据丢失。
  

Shell 脚本

什么是 Shell

Shell 是 Linux 的下令表明步调,是用户和内核之间的接口。除了作为下令表明步调外,Shell 同时还提供了一个可支持强盛脚本语言的步调环境。
Bash

Bourne Shell (sh),是 Unix 体系的默认 Shell,简单轻巧,脚本编程功能强,但交互性差。
Bourne Again Shell,即 Bash,是 GNU 开发的一个 Shell,也是大部分 Linux 体系的默认 Shell,是 Bourne Shell 的扩展。
Bash 允许用户定制环境以满足本身必要。通过修改环境文件 .bash_profile、.bashrc、.bash_logout,设置符合的环境变量,可以改变主目次、下令提示符、下令搜刮路径等用户工作环境。
别的,bash 也支持使用 alias 别名取代下令关键字(alias name='下令')。输入 alias,可以检察如今存在的别名:
  1. $ alias
  2. alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
  3. alias egrep='egrep --color=auto'
  4. alias fgrep='fgrep --color=auto'
  5. alias grep='grep --color=auto'
  6. alias l='ls -CF'
  7. alias la='ls -A'
  8. alias ll='ls -alF'
  9. alias ls='ls --color=auto'
  10. $ ll  # 执行 ll 相当于执行 ls -alF
  11. 总用量 128
  12. drwxr-xr-x 18 ustc ustc 4096 2月  28 00:51 ./
  13. drwxr-xr-x  3 root root 4096 11月 17 20:26 ../
  14. drwxr-xr-x  2 ustc ustc 4096 11月 17 20:45 公共的/
  15. drwxr-xr-x  2 ustc ustc 4096 11月 17 20:45 模板/
  16. (以下省略)
复制代码
其他 shell 的 alias
除了 bash 以外,其他的 shell 也有 alias 的支持。比方在 zsh 中也可以使用 alias 下令检察全部的 alias 列表。
部分 shell 会自带一些 alias,比方 fish 中的 ll 就是 ls -lh 的别名。特别地,Windows 自带的 PowerShell 中的 alias 存在一些争议,比方其对 curl 的 alias 现实上是 Invoke-WebRequest,而这个下令和上文先容的 curl 的活动完全差别,给用户带来了狐疑。
Bash 脚本的运行

   可以使用几种方法运行 Bash 脚本:
  

  • 在指定的 Shell 下实验,将脚本步调名作为 Shell 的第一个参数:
    1. $ bash show.sh [option]
    复制代码
  • 将脚本设置为可实验,然后像外部下令一样实验:
    1. $ chmod a+x show.sh
    2. $ ./show.sh [option]
    复制代码
关于 . 下令 
与直接实验脚本,大概指定 shell 实验脚本差别,使用 . 下令实验脚本会在当前 shell 中实验脚本,而不是新建一个 shell 去实验脚本。这意味着,脚本中的变量界说、函数界说等都会在当前 shell 中见效。
在 bash 中,source 下令与 . 下令等价。有些环境下,使用 . 实验脚本是有须要的,比方在激活 Python 的假造环境时
  1. . venv/bin/activate
复制代码
但是绝大多数时间,如果不清楚 . 或 source 下令的活动,不发起使用这种方式实验脚本。
   许多 Bash 脚本会在文件首行加上 #!/bin/bash 。这里 #! 符号的名称是 shebang(也叫 sha-bang,即 sharp # 与 bang !)。当一个文本文件首行有 shebang,且以可实验模式实验时,shebang 后的内容会看作这个脚本的表明器和相干参数,体系会实验表明器下令,并将脚本文件的路径作为参数转达给该下令。
  比方,某个 foo.sh 首活动 #!/bin/bash,则实验 ./foo.sh 就便是实验 /bin/bash ./foo.sh。
  Bash 也支持在同一个行中安排多个下令:
分隔符阐明;按下令出现的先后,次序实验&&先实验前面的下令,若乐成,才接着实验背面下令;若失败,不实验背面下令||先实验前面的下令,若乐成,不实验背面下令;若失败,才实验背面下令后缀 &背景方式实验下令  组下令:
  

  • 使用 { 下令1; 下令2; … },组下令在 shell 内实验,不会产生新的历程,注意花括号和下令之间的空格。
  • 使用 (下令1; 下令2; …),组下令会创建独立的 shell 子历程来实验组下令,这里的圆括号四周并不必要空格。
   组下令示例
  1. $ pwd  # 当前在家目录
  2. /home/ustc
  3. $ (cd /tmp; pwd;)
  4. /tmp
  5. $ pwd  # 仍然在家目录
  6. /home/ustc
  7. $ { cd /tmp; pwd; }
  8. /tmp
  9. $ pwd  # 移动到了 /tmp 目录
  10. /tmp
复制代码
实验组下令 { cd /tmp; pwd; } 后,当前目次会被修改,但是实验 (cd /tmp; pwd;) 不会修改当前目次。
  shell 变量

   像大多数步调计划语言一样,shell 也允许用户在步调中使用变量。但 shell 不支持数据范例,它将任何变量值都看成字符串。但从赋值情势上看,可将 shell 变量分成四种情势:用户自界说、环境变量、位置变量和预界说特别变量。
  1.用户自界说变量

   变量界说:name=串,此中 = 两边不允许有空格。如果字串中含空格,就要用双引号括起。在引用时,使用 $name 或 ${name},后者花括号是为了资助表明器辨认变量界限。
  已界说的变量可以通过 unset name 来删除。
  
  变量使用示例:
  变量界说:
  1. for skill in Ada Coffee Action Java; do
  2.     echo "I am good at ${skill}Script"
  3. done
复制代码
输出:
  1. I am good at AdaScript
  2. I am good at CoffeeScript
  3. I am good at ActionScript
  4. I am good at JavaScript
复制代码
如果不给 skill 加花括号标明变量名的界限,写成 echo "I am good at $skillScript" ,表明器就会把 $skillScript 当成一个变量(其值为空)。
  删除变量:
  1. Today=1024
  2. unset Today
  3. echo $Today
复制代码
输出为空。
  

   处理处罚未界说的变量
  在以上的例子中,我们可以注意到 bash 中未界说的变量默认值为空值。在编写 shell 脚本时,我们大概会盼望可以或许严格一些:如果变量未界说,就直接报错退出。如许的话,如果变量名出现了拼写错误,我们就能第一时间发现。
  可以在脚本开头加上 set -u 来实现这一点:
  1. #!/bin/bash
  2. set -u
  3. echo $nonexist
  4. echo "This will never be printed."
复制代码
实验可以发现输出雷同如下的错误:
  1. example.sh: line 4: nonexist: unbound variable
复制代码
  1. tom@linuxtom:~/work$ vim shell.shtom@linuxtom:~/work$ cat shell.sh#!/bin/bash
  2. set -u
  3. echo $nonexist
  4. echo "This will never be printed."
  5. tom@linuxtom:~/work$ ./shell.sh./shell.sh: 行 4: nonexist: 未绑定的变量
复制代码
2.环境变量

   每个用户登录体系后,Linux 都会为其创建一个默认的工作环境,由一组环境变量界说,用户可以通过修改这些环境变量,来定制本身工作环境。在 Bash 中,可用 env 下令列出全部已界说的环境变量。通常,用户最关注的几个变量是:
  

  • HOME:用户主目次,一样平常环境下为 /home/用户名。
  • LOGNAME:登任命户名。
  • PATH:下令搜刮路径,路径以冒号分割。当我们输入下令名时,体系会在 PATH 变量中从前以后逐个搜刮对应的步调是否在目次中。
  • PWD:用户当前工作目次路径。
  • SHELL:默认 shell 的路径名。
  • TERM:使用的终端名。
可以使用 export 下令来界说环境变量。在同一个 shell 中使用 export 界说之后,这个环境变量会不绝保存,直到这个 shell 退出。
  1. $ export A=1
  2. $ env | grep A=
  3. A=1
复制代码
别的,也可以在下令前加上环境变量的界说。此时只有这一条下令的环境变量出现厘革。
  1. $ B=1 env | grep B=
  2. B=1
  3. $ env | grep B=
  4. $ # B=1 的环境变量定义仅对该命令有效
复制代码
3.位置变量 

   

  • Shell 表明用户的下令时,把下令步调名背面的全部字串作为步调的参数。分别对应 $1、$2、$3、……、$9,步调名本身对应 $0。
  • 可用 shift <n> 下令,扬弃开头的 n 个位置变量,改变 $1、$2、$3 等的对应关系。
  • 可用 set 下令,重置整个位置变量列表,从而给 $1、$2、$3 等赋值。
范例:
  1. $ set one two three
  2. $ echo $1,$2,$3
  3. one,two,three
  4. $ shift 2
  5. $ echo $1,$2,$3
  6. three,,
  7. $ # 此时 $2 和 $3 已不存在
复制代码
4.特别变量 

   Shell 中另有一组有 shell 界说和设置的特别变量,用户只能引用,而不能直接改变或重置这些变量。
  特别变量阐明$#下令行上的参数个数,不包罗 $0$?末了下令的退出代码,0 表现乐成,别的值表现失败$$当进步程的 PID$!近来一个背景运行历程的历程号$*下令行全部参数构成的一个字符串$@用双引号括起的下令行各参数拼接构成的一个字符串 #特别字符

   

  • 反斜杠,消除单个字符的特别寄义。

    • 包罗空缺字符(空格和制表符)、反斜杠本身、各种引号,以及 $、! 等。
    • 与其他语言差别,shell 中反斜杠不会将平常字符转义为其他寄义(比方 \n 不会被视作换行符)。

  • 使用双引号包裹字符串可以消除空缺字符切分参数的特别寄义,但是许多其他特别字符的特别寄义仍旧保存。双引号也被称为「弱引用」。
  • 单引号,能消除全部特别字符的特别寄义,包罗反斜杠,因此单引号字符串中不能使用反斜杠转义单引号本身。单引号也被称为「强引用」
  • 反引号(`)括起的字符串,被 shell 表明为下令,实验时用下令输出结果取代整个反引号对界限部分。

    • 与反引号雷同的语法是 $(command),它的利益是界限更明白,且可以嵌套。因此编写新脚本时,更发起使用此语法。

特别字符示例:
  1. $ ls /mnt/c/Program Files/
  2. ls: cannot access /mnt/c/Program: No such file or directory
  3. ls: cannot access Files/: No such file or directory
  4. $ # 对于 ls 来说,它接收到了两个参数:/mnt/c/Program 和 Files/,因此会报错。
  5. $ # 可以使用反斜杠来转义空格
  6. $ ls /mnt/c/Program\ Files/  # 输出省略
  7. $ # 或者使用双引号或单引号包裹
  8. $ ls "/mnt/c/Program Files/"
  9. $ ls '/mnt/c/Program Files/'
  10. $ echo "$PWD"  # 双引号中仍然可以使用各种 shell 特殊符号
  11. /home/ustc
  12. $ echo '$PWD'  # 但是单引号则不行
  13. $PWD
  14. $ ls -lh `which ls`  # 查看 ls 命令对应的程序信息,使用反引号语法
  15. -rwxr-xr-x 1 root root 139K Sep  5  2019 /usr/bin/ls
  16. $ ls -lh $(which ls) # 使用 $(command) 语法也是一样的
  17. -rwxr-xr-x 1 root root 139K Sep  5  2019 /usr/bin/ls
复制代码
变量输入与输出

   输入

  可以使用 read 下令读取用户输入,并将输入的内容赋值给变量。此中 -p 参数后可以设置输出的提示信息。
  1. $ name=""
  2. $ read -p "Enter your name: " name  # 输出提示,然后从输入读取一个值,存储到 $name 中
  3. Enter your name: linux
  4. $ echo $name
  5. linux
复制代码
在使用 read 时,发起加上 -r 参数,否则 \ 会被视为转义符号
  1. $ message=""
  2. $ read -p "Enter your message: " message
  3. Enter your message: \(^o^)/~
  4. $ echo $message  # 可以看到,反斜杠被认为是转义符号,结果被丢弃
  5. (^o^)/~
  6. $ read -r -p "Enter your message: " message
  7. Enter your message: \(^o^)/~
  8. $ echo $message  # 加上 -r 参数后,反斜杠完好无损
  9. \(^o^)/~
复制代码
   输出 

  可以使用 echo 下令输出变量信息。此中 -n 参数不会在末端输出换行符,而 -e 参数会分析文本中的转义字符(比方 \n)
  1. $ echo -n "hello"
  2. hello$ # 由于这里 echo 结尾不输出换行,shell 就会在这里继续接受用户输入
  3. $ # 这也是为什么在 C 语言中最后的 printf 输出需要加上 \n 的原因
  4. $ name="linux"
  5. $ echo "Hello $name.\nWelcome to bash!"  # 可以看到 \n 没有被转义成换行
  6. Hello linux.\nWelcome to bash!
  7. $ echo -e "Hello $name.\nWelcome to bash!"  # 加上 -e 之后就被转义了
  8. Hello linux.
  9. Welcome to bash!
复制代码
别的,printf 下令也可以用来输出,它的使用方法雷同于 C 中的 printf() 函数。
  1. $ name="linux"
  2. $ printf "Hello %s" "$name"
  3. Hello linux$ # 和 echo 不同,printf 结尾默认不输出换行符
  4. $ printf "Hello %s\n" "$name"
  5. Hello linux
  6. $ # 所以为了正常显示,需要在结尾补上 \n
复制代码

算术运算 

   在 Bash 中举行算术运算,必要使用 expr 盘算算术表达式值或 let 下令赋值表达式值到变量。根本运算符是 +、-、\* (转义)、/、%。在 expr 中,运算符两边与利用数之间必须有空格,小括号要转义;但 let 则没有这个要求,运算符前后有无空格均可,小括号不需转义,但 = 前后不能有空格。
  别的,全部标准的 shell 都支持另一种语法 (( 表达式 )),此中 表达式 是一个 C 风格的数学表达式,可以盘算,也可以赋值。(( 表达式 )) 是一条完备的下令,下令的返回值为 0 或 1。若表达式的结果非零,那么 (( 表达式 )) 下令返回零,而当表达式结果为零时下令返回 1。
  (( 表达式 )) 的返回值
  1. $ (( 1 + 1 ))
  2. $ echo $?  # 结果为 2,所以返回值为 0  ## $? 最后命令的退出代码,0 表示成功,其它值表示失败
  3. 0
  4. $ (( 1 - 1 ))
  5. $ echo $?  # 返回值为 1,因为结果是 0
  6. 1
复制代码
使用 $(( 表达式 )) 可以将盘算结果用作为下令行的一部分,就像使用变量一样。 
  expr 和 let 使用示例:
  1. $ expr length "ustclug"
  2. 7
  3. $ let a=0
  4. $ echo $a
  5. 0
  6. $ let a++
  7. $ echo $a
  8. 1
  9. $ ((a+=1))
  10. $ echo $a
  11. 2
  12. $ echo $((a+=a/a))
  13. 3
复制代码

条件表达式

   条件表达式写成 test 条件表达式,或 [ 条件表达式 ],注意表达式与方括号之间有空格
  字符串比力

表达式寄义string1 = string2如果两个串相称,则结果为真(true: 0)string1 != string2如果两个串不相称,则结果为真string 或 -n string如果字符串 string 长度不为 0,则结果为真-z string如果字符串 string 长度为 0,则结果为真数值比力

表达式:int1 [option] int2,此中的参数可以用下列更换。
参数阐明-eq便是-ne未便是-gt大于-ge大于便是-lt小于-le小于便是文件状态

表达式寄义-r file文件存在且可读-w file文件存在且可写-x file文件存在且可实验-f file文件存在且为平常文件-d file文件存在且为目次-s file文件存在且长度大于 0复合逻辑表达式

表达式寄义! expr否运算expr1 –a expr2与运算expr1 –o expr2或运算流程控制

   条件分支:if

  序列中可嵌套 if 语句,在 shell 中也允许有多个 elif ,但 shell 的流程控制不可为空。末了的 fi 就是 if 倒过来写,背面还会遇到雷同的竣事符。
  1. if condition1
  2. then
  3.   commands1
  4. elif condition2
  5. then
  6.   commands2
  7. else
  8.   commands3
  9. fi
复制代码
   按值选择:case 

  选项值必须以右括号 ) 末端,若匹配多个离散值,用 | 分隔。这里的 esac 也是 case 倒着写。
  1. case <variable> in
  2. value1|value2)
  3.   command1
  4.   command2
  5.   ;;
  6. value3)
  7.   command3
  8.   ;;
  9. *)
  10.   command4
  11.   ;;
  12. esac
复制代码
   摆列循环:for 

  for 循环可以对一个列表中的每个值都实验一系列下令。
  1. for var in list
  2. do
  3.   commands $var
  4. done
复制代码
条件循环:while 和 until 

  while 循环用于不绝实验一系列下令,下令通常为测试条件。until 与 while 相反,仅在测试条件失败时循环。
  1. while condition
  2. do
  3.   commands
  4. done
  5. until condition
  6. do
  7.   commands
  8. done
复制代码
    流程控制样例:
  1. #!/bin/bash
  2. MAX_NO=0
  3. read -r -p "Enter Number between (5 to 9) : " MAX_NO
  4. if ! [ "$MAX_NO" -ge 5 -a "$MAX_NO" -le 9 ] ; then
  5.   echo "I ask to enter number between 5 and 9, Okay"
  6.   exit 1
  7. fi
  8. clear
  9. for (( i=1; i<=MAX_NO; i++ ))
  10. do
  11.   for (( s=MAX_NO; s>=i; s-- ))
  12.   do
  13.     echo -n " "
  14.   done
  15.   for (( j=1; j<=i;  j++ ))
  16.   do
  17.     echo -n " ."
  18.   done
  19.   echo ""
  20. done
  21. for (( i=MAX_NO; i>=1; i-- ))
  22. do
  23.   for (( s=i; s<=MAX_NO; s++ ))
  24.   do
  25.     echo -n " "
  26.   done
  27.   for (( j=1; j<=i;  j++ ))
  28.   do
  29.     echo -n " ."
  30.   done
  31.   echo ""
  32. done
复制代码
输出结果:
  1. Enter Number between (5 to 9) : 9
  2.          .
  3.         . .
  4.        . . .
  5.       . . . .
  6.      . . . . .
  7.     . . . . . .
  8.    . . . . . . .
  9.   . . . . . . . .
  10. . . . . . . . . .
  11. . . . . . . . . .
  12.   . . . . . . . .
  13.    . . . . . . .
  14.     . . . . . .
  15.      . . . . .
  16.       . . . .
  17.        . . .
  18.         . .
  19.          .
复制代码
除此之外,用于流程控制的另有在 C 语言中同样常见的 break 和 continue。与 C 语言差别的是,它们还担当一个数字作为参数,即 break n 和 continue n,此中参数 n 均表现跳出或跳过 n 层循环。
函数

   与其他编程语言雷同,shell 也可以界说函数。其界说格式为:
  1. # POSIX syntax
  2. name() {
  3.     commands
  4.     [return <int>]
  5. }
  6. # Bash syntax
  7. function name {
  8.     commands
  9.     [return <int>]
  10. }
复制代码
此中函数的参数可以像下令一样通过 $1、$2、$3 等获取,返回值可以显式用 return 返回,或以末了一条下令运行结果作为返回值。在函数中使用 return 会竣事本次函数实验,而使用 exit 会直接竣事退出包罗函数的当前脚本步调。
  函数在使用前必须界说,因此应将函数界说放在脚本开始的部分。在调用函数时仅使用其函数名即可。
  
  范例:
  某带函数的某脚本步调内容如下:
  1. #!/bin/bash
  2. hello() {
  3.   echo "hello $1, today's date is `date`"
  4. }
  5. echo "going to call test function:"
  6. hello ustc
复制代码
运行脚本,输出结果:
  1. going to call test function:
  2. hello ustc, today's date is Tue Feb 22 22:22:22 CST 2022
复制代码
Bash 脚本调试

   Bash shell 本身提供了调试方法:
  

  • 下令行中:$ bash -x script.sh。
  • 脚本开头:#!/bin/bash -x。
  • 在脚本中用 set 下令调解(set -x 启用,set +x 禁用)。
此中参数选项可以更改,-n:读一遍脚本中的下令但不实验,用于查抄语法错误;-v:一边实验脚本、一边将实验过的脚本下令打印到标准输出;-x:提供跟踪实验信息,将实验的每一条下令和结果依次打印出来。注意克制几种调试选项混用。
  除了 Bash shell 内置的选项,另有 BASH Debugger、shellcheck 品级三方脚天职析工具。
  

拓展阅读

   类 C 语言的 for 循环

  1. for((assignment;condition;next));do
  2.     command_1;
  3.     command_2;
  4. done;
复制代码
这里的 for 循环与 C 中的相似。我们知道,在 shell 里变量调用必要加 $,但是 for 中的 (()) 中不必要。
  范例
  shell 脚本内容:
  1. #!/bin/bash
  2. for((i=1;i<=5;i++));do
  3.     echo "这是第 $i 次调用";
  4. done;
复制代码
输出结果:
  1. 这是第 1 次调用
  2. 这是第 2 次调用
  3. 这是第 3 次调用
  4. 这是第 4 次调用
  5. 这是第 5 次调用
复制代码
与 C 中相似,赋值和下一步实验可以放到代码之前循环语句之中实验,这里要注意一点:如果要在循环体中举行 for 中的 next 利用,记得变量要加 $,否则步调会变成死循环。
  Fork 炸弹原理分析

   下令含有伤害性
  以下的下令均包罗肯定的伤害性,请在任何环境下都不要实验,除非你清楚你在做什么。
  在第五章中,我们先容过 fork 炸弹。它通过创建大量历程斲丧大量的体系资源,从而拖慢体系与正常历程的运行速率,增大相应时间,使得利用体系的正常运作受到较大影响。1
  原理

  Fork 炸弹有如下的这种情势:
  1. :(){ :|: & };:
复制代码
这是一个函数界说以及对其的调用语句,可以格式化为:
  1. :()
  2. {
  3.     :|: &
  4. };
  5. :
复制代码
在 Bash 中,:、.、/ 等一些字符也可以或许被用于函数定名,因此,上面的代码等价于:
  1. func()
  2. {
  3.     func | func &
  4. };
  5. func
复制代码
fork 炸弹的核心是函数内容:func | func &
  

  • 第一个 func 代表递归实验这个函数。
  • | 代表要将第一个函数的数据结果通过管道传输给后一个函数。
  • & 代表要在背景实验这一条下令,假云云中一个函数被利用体系接纳,其调用产生的子函数并不会被接纳。
于是运行一次这个函数就会创建两个 func 函数的实例,并不绝地反复调用。实例的数目会指数爆炸式地增长,终极耗尽体系的资源。
  防范方法

  一个有用的方式1是通过修改体系设置,限定一个用户可以或许拥有的历程数目多少。ulimit -u 30 可以限定当前用户可以或许拥有的历程数目为 30。
  引用泉源Fork Bomb
https://en.wikipedia.org/wiki/Fork_bomb


思考题

   I/O 重定向的小细节
  wc -l file 和 wc -l < file 输出有什么区别?为什么?
  echo < file 会输出什么?
    设定 HTTP 哀求头
  实验查询 curl 和 wget 的文档,给出设定 HTTP 哀求头的方法。
  附:HTTP 在哀求时,会发送诸如以下的信息:
  1. GET / HTTP/1.1
  2. Host: cn.bing.com
  3. User-Agent: curl/7.54.0
  4. Accept: */*
复制代码
在 GET / HTTP/1.1 下面的几行就是 HTTP 哀求头。
  HTTP 哀求头在向服务器发送 HTTP 哀求时发出,包罗了诸如访问服务的域名(Host)、用户步调标识(User-Agent)、盼望吸收到的语言(Accept-Language)等各种信息。
    关于断点续传
  查找资料,相识下载器的「断点续传」功能依赖于什么 HTTP 特性?
  /usr/bin/env
  你大概会发现,某些脚本(不但仅是 shell 脚本)的第一行开头为 #!/usr/bin/env。实验表明缘故原由。
    Shell 脚本编写训练 #1
  ffmpeg 步调可以提取视频中的音频流,并输出到音频格式文件(如 MP3)。
  如今,你下载了许多视频在目次下(可以假设后缀雷同,比方都是 flv)。你必要提取这些视频的音频轨道到 MP3 文件中。实验搜刮 ffmpeg 的使用资料,编写一个 shell 脚本实现。
    Shell 脚本编写训练 #2
  现有两个步调 ./a 和 ./b,它们分别担当一个数字作为参数。如今必要编写一个脚本,要求:
  

  • 查抄输入的第一个参数 (设为 x) 是否存在。如果不是,输出提示,竣事。
  • 先实验 ./a,以 x 作为第一个参数。如果 ./a 实验乐成,就实验 ./b,以 x 的平方作为第二个参数。
假设输入 x 是数字且不大。
    Shell 脚本编写训练 #3 (难)
  发起阅读第九章后再实验完资源题。
  实验编写一个 shell 脚本,下载某个网页上全部的 PDF 文件(比方 2019年春季全校《电磁学》小论文比赛获奖名单 这个网页)。已知全部的文件都以小写的 .pdf 末端,而且都在 a 标签的 href 属性中。
  引用泉源



  • catonmat
  • vbird
  • runoob
  • linuxde
  • Bash Quoting
参考

        USTC--101讲义 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表