文本处理处罚
在举行文本处理处罚时,我们有一些常见的需求:
- 获取文本的行数、字数
- 比力两段文本的差别之处
- 检察文本的开头几行和末了几行
- 在文本中查找字符串
- 在文本中更换字符串
下面先容怎样在 shell 中做到这些事故。
文本统计:wc
wc 是文本统计的常用工具,它可以输出文本的行数、单词数与字符(字节)数。
- $ wc file
- 427 2768 20131 file
复制代码 #统计中文文本时的标题
wc 在统计中文文本时,会出现一些标题,好比:
这里表现文本只有 1 个单词,但是有 13 个字符,这显然是不对的。
对于字符数统计结果,可以使用 wc -m 下令要求 wc 思量宽字符:
换行符也是一个符号,以是结果为 5(而非 4)。
由于中文文本的单词统计涉及分词算法标题,wc 无法正确统计。
文本比力:diff
diff 工具用于比力两个文件的差别,并列出差别。
- $ echo hello > file1
- $ echo hallo > file2
- $ diff file1 file1
- $ diff file1 file2
- 1c1
- < hello
- ---
- > hallo
复制代码 小知识
加参数 -w 可忽略全部空缺字符, -b 可忽略空缺字符的数目厘革。
如果比力的是两个文本文件,差别之处会被列出;如果比力的是二进制文件,只会指出是否有差别。
文本开头与末端:head & tail
顾名思义,head 和 tail 分别用来表现开头和末端指定命量的笔墨。
以 head 为例,这里给出共同的用法:
- 不加参数的时间默认表现前 10 行
- -n <NUM> 指定行数,可简化为 -<NUM>
- -c <NUM> 指定字节数
- $ head file # 显示 file 前 10 行
- $ head -n 25 file # 显示 file 前 25 行
- $ head -25 file # 显示 file 前 25 行
- $ head -c 20 file # 显示 file 前 20 个字符
- $ tail -10 file # 显示 file 最后 10 行
复制代码 除此以外,tail 另有一个非常实用的参数 -f:当文件末了内容增长时,连续输出末了增长的内容。这个参数常用于动态表现 log 文件的更新(试一试 tail -f /var/log/syslog)
文本查找:grep
grep 下令可以查找文本中的字符串:
- $ grep 'hello' file # 查找文件 file 中包含 hello 的行
- $ ls | grep 'file' # 查找当前目录下文件名包含 file 的文件
- $ grep -i 'Systemd' file # 查找文件 file 中包含 Systemd 的行(忽略大小写)
- $ grep -R 'hello' . # 递归查找当前目录下内容包含 hello 的文件
复制代码 不止云云!
grep 毕竟上黑白常强盛的查找工具,第九章将在先容正则表达式语法之后进一步先容 grep。
文本更换:sed
sed 下令可以更换文本中的字符串:
- $ sed 's/hello/world/g' file # 将文件 file 中的 hello 全局(global)替换为 world 后输出
- $ sed 's/hello/world/' file # 将文件 file 的每一行第一个出现的 hello 替换为 world 后输出
- $ echo 'helloworld' | sed 's/hello/world/g' # 管道也是可以的
- $ sed -i 's/hello/world/g' file # -i 参数会直接写入文件,操作前记得备份哦!
- $ sed -i.bak 's/hello/world/g' file # 当然,也可以让 sed 帮你备份到 file.bak
复制代码 对于大多数用户来说,最常用 sed 的场合是更换软件源的时间。在阅读了上面的示例之后,以下例子就很简单了:
- $ sudo sed -i 's/cn.archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
- $ sudo sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
- $ 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,可以检察如今存在的别名:
- $ alias
- 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$//'\'')"'
- alias egrep='egrep --color=auto'
- alias fgrep='fgrep --color=auto'
- alias grep='grep --color=auto'
- alias l='ls -CF'
- alias la='ls -A'
- alias ll='ls -alF'
- alias ls='ls --color=auto'
- $ ll # 执行 ll 相当于执行 ls -alF
- 总用量 128
- drwxr-xr-x 18 ustc ustc 4096 2月 28 00:51 ./
- drwxr-xr-x 3 root root 4096 11月 17 20:26 ../
- drwxr-xr-x 2 ustc ustc 4096 11月 17 20:45 公共的/
- drwxr-xr-x 2 ustc ustc 4096 11月 17 20:45 模板/
- (以下省略)
复制代码 其他 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 的第一个参数:
- 将脚本设置为可实验,然后像外部下令一样实验:
- $ chmod a+x show.sh
- $ ./show.sh [option]
复制代码 关于 . 下令
与直接实验脚本,大概指定 shell 实验脚本差别,使用 . 下令实验脚本会在当前 shell 中实验脚本,而不是新建一个 shell 去实验脚本。这意味着,脚本中的变量界说、函数界说等都会在当前 shell 中见效。
在 bash 中,source 下令与 . 下令等价。有些环境下,使用 . 实验脚本是有须要的,比方在激活 Python 的假造环境时:
但是绝大多数时间,如果不清楚 . 或 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 子历程来实验组下令,这里的圆括号四周并不必要空格。
组下令示例
- $ pwd # 当前在家目录
- /home/ustc
- $ (cd /tmp; pwd;)
- /tmp
- $ pwd # 仍然在家目录
- /home/ustc
- $ { cd /tmp; pwd; }
- /tmp
- $ pwd # 移动到了 /tmp 目录
- /tmp
复制代码 实验组下令 { cd /tmp; pwd; } 后,当前目次会被修改,但是实验 (cd /tmp; pwd;) 不会修改当前目次。
shell 变量
像大多数步调计划语言一样,shell 也允许用户在步调中使用变量。但 shell 不支持数据范例,它将任何变量值都看成字符串。但从赋值情势上看,可将 shell 变量分成四种情势:用户自界说、环境变量、位置变量和预界说特别变量。
1.用户自界说变量
变量界说:name=串,此中 = 两边不允许有空格。如果字串中含空格,就要用双引号括起。在引用时,使用 $name 或 ${name},后者花括号是为了资助表明器辨认变量界限。
已界说的变量可以通过 unset name 来删除。
变量使用示例:
变量界说:
- for skill in Ada Coffee Action Java; do
- echo "I am good at ${skill}Script"
- done
复制代码 输出:
- I am good at AdaScript
- I am good at CoffeeScript
- I am good at ActionScript
- I am good at JavaScript
复制代码 如果不给 skill 加花括号标明变量名的界限,写成 echo "I am good at $skillScript" ,表明器就会把 $skillScript 当成一个变量(其值为空)。
删除变量:
- Today=1024
- unset Today
- echo $Today
复制代码 输出为空。
处理处罚未界说的变量
在以上的例子中,我们可以注意到 bash 中未界说的变量默认值为空值。在编写 shell 脚本时,我们大概会盼望可以或许严格一些:如果变量未界说,就直接报错退出。如许的话,如果变量名出现了拼写错误,我们就能第一时间发现。
可以在脚本开头加上 set -u 来实现这一点:
- #!/bin/bash
- set -u
- echo $nonexist
- echo "This will never be printed."
复制代码 实验可以发现输出雷同如下的错误:
- example.sh: line 4: nonexist: unbound variable
复制代码- tom@linuxtom:~/work$ vim shell.shtom@linuxtom:~/work$ cat shell.sh#!/bin/bash
- set -u
- echo $nonexist
- echo "This will never be printed."
- 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 退出。
- $ export A=1
- $ env | grep A=
- A=1
复制代码 别的,也可以在下令前加上环境变量的界说。此时只有这一条下令的环境变量出现厘革。
- $ B=1 env | grep B=
- B=1
- $ env | grep B=
- $ # B=1 的环境变量定义仅对该命令有效
复制代码 3.位置变量
- Shell 表明用户的下令时,把下令步调名背面的全部字串作为步调的参数。分别对应 $1、$2、$3、……、$9,步调名本身对应 $0。
- 可用 shift <n> 下令,扬弃开头的 n 个位置变量,改变 $1、$2、$3 等的对应关系。
- 可用 set 下令,重置整个位置变量列表,从而给 $1、$2、$3 等赋值。
范例:
- $ set one two three
- $ echo $1,$2,$3
- one,two,three
- $ shift 2
- $ echo $1,$2,$3
- three,,
- $ # 此时 $2 和 $3 已不存在
复制代码 4.特别变量
Shell 中另有一组有 shell 界说和设置的特别变量,用户只能引用,而不能直接改变或重置这些变量。
特别变量阐明$#下令行上的参数个数,不包罗 $0$?末了下令的退出代码,0 表现乐成,别的值表现失败$$当进步程的 PID$!近来一个背景运行历程的历程号$*下令行全部参数构成的一个字符串$@用双引号括起的下令行各参数拼接构成的一个字符串 #特别字符
- 反斜杠,消除单个字符的特别寄义。
- 包罗空缺字符(空格和制表符)、反斜杠本身、各种引号,以及 $、! 等。
- 与其他语言差别,shell 中反斜杠不会将平常字符转义为其他寄义(比方 \n 不会被视作换行符)。
- 使用双引号包裹字符串可以消除空缺字符切分参数的特别寄义,但是许多其他特别字符的特别寄义仍旧保存。双引号也被称为「弱引用」。
- 单引号,能消除全部特别字符的特别寄义,包罗反斜杠,因此单引号字符串中不能使用反斜杠转义单引号本身。单引号也被称为「强引用」。
- 反引号(`)括起的字符串,被 shell 表明为下令,实验时用下令输出结果取代整个反引号对界限部分。
- 与反引号雷同的语法是 $(command),它的利益是界限更明白,且可以嵌套。因此编写新脚本时,更发起使用此语法。
特别字符示例:
- $ ls /mnt/c/Program Files/
- ls: cannot access /mnt/c/Program: No such file or directory
- ls: cannot access Files/: No such file or directory
- $ # 对于 ls 来说,它接收到了两个参数:/mnt/c/Program 和 Files/,因此会报错。
- $ # 可以使用反斜杠来转义空格
- $ ls /mnt/c/Program\ Files/ # 输出省略
- $ # 或者使用双引号或单引号包裹
- $ ls "/mnt/c/Program Files/"
- $ ls '/mnt/c/Program Files/'
- $ echo "$PWD" # 双引号中仍然可以使用各种 shell 特殊符号
- /home/ustc
- $ echo '$PWD' # 但是单引号则不行
- $PWD
- $ ls -lh `which ls` # 查看 ls 命令对应的程序信息,使用反引号语法
- -rwxr-xr-x 1 root root 139K Sep 5 2019 /usr/bin/ls
- $ ls -lh $(which ls) # 使用 $(command) 语法也是一样的
- -rwxr-xr-x 1 root root 139K Sep 5 2019 /usr/bin/ls
复制代码 变量输入与输出
输入
可以使用 read 下令读取用户输入,并将输入的内容赋值给变量。此中 -p 参数后可以设置输出的提示信息。
- $ name=""
- $ read -p "Enter your name: " name # 输出提示,然后从输入读取一个值,存储到 $name 中
- Enter your name: linux
- $ echo $name
- linux
复制代码 在使用 read 时,发起加上 -r 参数,否则 \ 会被视为转义符号。
- $ message=""
- $ read -p "Enter your message: " message
- Enter your message: \(^o^)/~
- $ echo $message # 可以看到,反斜杠被认为是转义符号,结果被丢弃
- (^o^)/~
- $ read -r -p "Enter your message: " message
- Enter your message: \(^o^)/~
- $ echo $message # 加上 -r 参数后,反斜杠完好无损
- \(^o^)/~
复制代码 输出
可以使用 echo 下令输出变量信息。此中 -n 参数不会在末端输出换行符,而 -e 参数会分析文本中的转义字符(比方 \n)。
- $ echo -n "hello"
- hello$ # 由于这里 echo 结尾不输出换行,shell 就会在这里继续接受用户输入
- $ # 这也是为什么在 C 语言中最后的 printf 输出需要加上 \n 的原因
- $ name="linux"
- $ echo "Hello $name.\nWelcome to bash!" # 可以看到 \n 没有被转义成换行
- Hello linux.\nWelcome to bash!
- $ echo -e "Hello $name.\nWelcome to bash!" # 加上 -e 之后就被转义了
- Hello linux.
- Welcome to bash!
复制代码 别的,printf 下令也可以用来输出,它的使用方法雷同于 C 中的 printf() 函数。
- $ name="linux"
- $ printf "Hello %s" "$name"
- Hello linux$ # 和 echo 不同,printf 结尾默认不输出换行符
- $ printf "Hello %s\n" "$name"
- Hello linux
- $ # 所以为了正常显示,需要在结尾补上 \n
复制代码
算术运算
在 Bash 中举行算术运算,必要使用 expr 盘算算术表达式值或 let 下令赋值表达式值到变量。根本运算符是 +、-、\* (转义)、/、%。在 expr 中,运算符两边与利用数之间必须有空格,小括号要转义;但 let 则没有这个要求,运算符前后有无空格均可,小括号不需转义,但 = 前后不能有空格。
别的,全部标准的 shell 都支持另一种语法 (( 表达式 )),此中 表达式 是一个 C 风格的数学表达式,可以盘算,也可以赋值。(( 表达式 )) 是一条完备的下令,下令的返回值为 0 或 1。若表达式的结果非零,那么 (( 表达式 )) 下令返回零,而当表达式结果为零时下令返回 1。
(( 表达式 )) 的返回值
- $ (( 1 + 1 ))
- $ echo $? # 结果为 2,所以返回值为 0 ## $? 最后命令的退出代码,0 表示成功,其它值表示失败
- 0
- $ (( 1 - 1 ))
- $ echo $? # 返回值为 1,因为结果是 0
- 1
复制代码 使用 $(( 表达式 )) 可以将盘算结果用作为下令行的一部分,就像使用变量一样。
expr 和 let 使用示例:
- $ expr length "ustclug"
- 7
- $ let a=0
- $ echo $a
- 0
- $ let a++
- $ echo $a
- 1
- $ ((a+=1))
- $ echo $a
- 2
- $ echo $((a+=a/a))
- 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 倒过来写,背面还会遇到雷同的竣事符。
- if condition1
- then
- commands1
- elif condition2
- then
- commands2
- else
- commands3
- fi
复制代码 按值选择:case
选项值必须以右括号 ) 末端,若匹配多个离散值,用 | 分隔。这里的 esac 也是 case 倒着写。
- case <variable> in
- value1|value2)
- command1
- command2
- ;;
- value3)
- command3
- ;;
- *)
- command4
- ;;
- esac
复制代码 摆列循环:for
for 循环可以对一个列表中的每个值都实验一系列下令。
- for var in list
- do
- commands $var
- done
复制代码 条件循环:while 和 until
while 循环用于不绝实验一系列下令,下令通常为测试条件。until 与 while 相反,仅在测试条件失败时循环。
- while condition
- do
- commands
- done
- until condition
- do
- commands
- done
复制代码 流程控制样例:
- #!/bin/bash
- MAX_NO=0
- read -r -p "Enter Number between (5 to 9) : " MAX_NO
- if ! [ "$MAX_NO" -ge 5 -a "$MAX_NO" -le 9 ] ; then
- echo "I ask to enter number between 5 and 9, Okay"
- exit 1
- fi
- clear
- for (( i=1; i<=MAX_NO; i++ ))
- do
- for (( s=MAX_NO; s>=i; s-- ))
- do
- echo -n " "
- done
- for (( j=1; j<=i; j++ ))
- do
- echo -n " ."
- done
- echo ""
- done
- for (( i=MAX_NO; i>=1; i-- ))
- do
- for (( s=i; s<=MAX_NO; s++ ))
- do
- echo -n " "
- done
- for (( j=1; j<=i; j++ ))
- do
- echo -n " ."
- done
- echo ""
- done
复制代码 输出结果:
- Enter Number between (5 to 9) : 9
- .
- . .
- . . .
- . . . .
- . . . . .
- . . . . . .
- . . . . . . .
- . . . . . . . .
- . . . . . . . . .
- . . . . . . . . .
- . . . . . . . .
- . . . . . . .
- . . . . . .
- . . . . .
- . . . .
- . . .
- . .
- .
复制代码 除此之外,用于流程控制的另有在 C 语言中同样常见的 break 和 continue。与 C 语言差别的是,它们还担当一个数字作为参数,即 break n 和 continue n,此中参数 n 均表现跳出或跳过 n 层循环。
函数
与其他编程语言雷同,shell 也可以界说函数。其界说格式为:
- # POSIX syntax
- name() {
- commands
- [return <int>]
- }
- # Bash syntax
- function name {
- commands
- [return <int>]
- }
复制代码 此中函数的参数可以像下令一样通过 $1、$2、$3 等获取,返回值可以显式用 return 返回,或以末了一条下令运行结果作为返回值。在函数中使用 return 会竣事本次函数实验,而使用 exit 会直接竣事退出包罗函数的当前脚本步调。
函数在使用前必须界说,因此应将函数界说放在脚本开始的部分。在调用函数时仅使用其函数名即可。
范例:
某带函数的某脚本步调内容如下:
- #!/bin/bash
- hello() {
- echo "hello $1, today's date is `date`"
- }
- echo "going to call test function:"
- hello ustc
复制代码 运行脚本,输出结果:
- going to call test function:
- 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 循环
- for((assignment;condition;next));do
- command_1;
- command_2;
- done;
复制代码 这里的 for 循环与 C 中的相似。我们知道,在 shell 里变量调用必要加 $,但是 for 中的 (()) 中不必要。
范例
shell 脚本内容:
- #!/bin/bash
- for((i=1;i<=5;i++));do
- echo "这是第 $i 次调用";
- done;
复制代码 输出结果:
- 这是第 1 次调用
- 这是第 2 次调用
- 这是第 3 次调用
- 这是第 4 次调用
- 这是第 5 次调用
复制代码 与 C 中相似,赋值和下一步实验可以放到代码之前循环语句之中实验,这里要注意一点:如果要在循环体中举行 for 中的 next 利用,记得变量要加 $,否则步调会变成死循环。
Fork 炸弹原理分析
下令含有伤害性
以下的下令均包罗肯定的伤害性,请在任何环境下都不要实验,除非你清楚你在做什么。
在第五章中,我们先容过 fork 炸弹。它通过创建大量历程斲丧大量的体系资源,从而拖慢体系与正常历程的运行速率,增大相应时间,使得利用体系的正常运作受到较大影响。1
原理
Fork 炸弹有如下的这种情势:
这是一个函数界说以及对其的调用语句,可以格式化为:
在 Bash 中,:、.、/ 等一些字符也可以或许被用于函数定名,因此,上面的代码等价于:
- func()
- {
- func | func &
- };
- func
复制代码 fork 炸弹的核心是函数内容:func | func &
- 第一个 func 代表递归实验这个函数。
- | 代表要将第一个函数的数据结果通过管道传输给后一个函数。
- & 代表要在背景实验这一条下令,假云云中一个函数被利用体系接纳,其调用产生的子函数并不会被接纳。
于是运行一次这个函数就会创建两个 func 函数的实例,并不绝地反复调用。实例的数目会指数爆炸式地增长,终极耗尽体系的资源。
防范方法
一个有用的方式1是通过修改体系设置,限定一个用户可以或许拥有的历程数目多少。ulimit -u 30 可以限定当前用户可以或许拥有的历程数目为 30。
引用泉源Fork Bombhttps://en.wikipedia.org/wiki/Fork_bomb
思考题
I/O 重定向的小细节
wc -l file 和 wc -l < file 输出有什么区别?为什么?
echo < file 会输出什么?
设定 HTTP 哀求头
实验查询 curl 和 wget 的文档,给出设定 HTTP 哀求头的方法。
附:HTTP 在哀求时,会发送诸如以下的信息:
- GET / HTTP/1.1
- Host: cn.bing.com
- User-Agent: curl/7.54.0
- 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企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金 |