shell 技巧

😀
这里写文章的前言: 一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么?并简述你给出的答案。
可以说说你的故事:阻碍、努力、结果成果,意外与转折。

📝主旨内容

bash和env调用的区别

定位脚本的路径

notion image

cat小技巧

调用脚本在外修改环境变量

 

Bash 脚本 set 命令教程


created: 2023-03-07T10:36:27 (UTC +08:00) tags: [] source: https://www.ruanyifeng.com/blog/2017/11/bash-set.html author: 作者: 阮一峰

Bash 脚本 set 命令教程 - 阮一峰的网络日志

Excerpt
服务器的开发和管理离不开 Bash 脚本,掌握它需要学习大量的细节。

服务器的开发和管理离不开 Bash 脚本,掌握它需要学习大量的细节。
notion image
set命令是 Bash 脚本的重要环节,却常常被忽视,导致脚本的安全性和可维护性出问题。本文介绍它的基本用法,让你可以更安心地使用 Bash 脚本。

一、简介

我们知道,Bash 执行脚本的时候,会创建一个新的 Shell。
$ bash script.sh
上面代码中,script.sh是在一个新的 Shell 里面执行。这个 Shell 就是脚本的执行环境,Bash 默认给定了这个环境的各种参数。
set命令用来修改 Shell 环境的运行参数,也就是可以定制环境。一共有十几个参数可以定制,官方手册有完整清单,本文介绍其中最常用的四个。
顺便提一下,如果命令行下不带任何参数,直接运行set,会显示所有的环境变量和 Shell 函数。
$ set

二、set -u

执行脚本的时候,如果遇到不存在的变量,Bash 默认忽略它。
#!/usr/bin/env bash echo $a echo bar
上面代码中,$a是一个不存在的变量。执行结果如下。
$ bash script.sh bar
可以看到,echo $a输出了一个空行,Bash 忽略了不存在的$a,然后继续执行echo bar。大多数情况下,这不是开发者想要的行为,遇到变量不存在,脚本应该报错,而不是一声不响地往下执行。
set -u就用来改变这种行为。脚本在头部加上它,遇到不存在的变量就会报错,并停止执行。
#!/usr/bin/env bash set -u echo $a echo bar
运行结果如下。
$ bash script.sh bash: script.sh:行4: a: 未绑定的变量
可以看到,脚本报错了,并且不再执行后面的语句。
  • u还有另一种写法o nounset,两者是等价的。
set -o nounset

三、set -x

默认情况下,脚本执行后,屏幕只显示运行结果,没有其他内容。如果多个命令连续执行,它们的运行结果就会连续输出。有时会分不清,某一段内容是什么命令产生的。
set -x用来在运行结果之前,先输出执行的那一行命令。
#!/usr/bin/env bash set -x echo bar
执行上面的脚本,结果如下。
$ bash script.sh + echo bar bar
可以看到,执行echo bar之前,该命令会先打印出来,行首以+表示。这对于调试复杂的脚本是很有用的。
  • x还有另一种写法o xtrace
set -o xtrace

四、Bash 的错误处理

如果脚本里面有运行失败的命令(返回值非0),Bash 默认会继续执行后面的命令。
#!/usr/bin/env bash foo echo bar
上面脚本中,foo是一个不存在的命令,执行时会报错。但是,Bash 会忽略这个错误,继续往下执行。
$ bash script.sh script.sh:行3: foo: 未找到命令 bar
可以看到,Bash 只是显示有错误,并没有终止执行。
这种行为很不利于脚本安全和除错。实际开发中,如果某个命令失败,往往需要脚本停止执行,防止错误累积。这时,一般采用下面的写法。
command || exit 1
上面的写法表示只要command有非零返回值,脚本就会停止执行。
如果停止执行之前需要完成多个操作,就要采用下面三种写法。
# 写法一 command || { echo "command failed"; exit 1; } # 写法二 if ! command; then echo "command failed"; exit 1; fi # 写法三 command if [ "$?" -ne 0 ]; then echo "command failed"; exit 1; fi
另外,除了停止执行,还有一种情况。如果两个命令有继承关系,只有第一个命令成功了,才能继续执行第二个命令,那么就要采用下面的写法。
command1 && command2

五、 set -e

上面这些写法多少有些麻烦,容易疏忽。set -e从根本上解决了这个问题,它使得脚本只要发生错误,就终止执行。
#!/usr/bin/env bash set -e foo echo bar
执行结果如下。
$ bash script.sh script.sh:行4: foo: 未找到命令
可以看到,第4行执行失败以后,脚本就终止执行了。
set -e根据返回值来判断,一个命令是否运行失败。但是,某些命令的非零返回值可能不表示失败,或者开发者希望在命令失败的情况下,脚本继续执行下去。这时可以暂时关闭set -e,该命令执行结束后,再重新打开set -e
set +e command1 command2 set -e
上面代码中,set +e表示关闭-e选项,set -e表示重新打开-e选项。
还有一种方法是使用command || true,使得该命令即使执行失败,脚本也不会终止执行。
#!/bin/bash set -e foo || true echo bar
上面代码中,true使得这一行语句总是会执行成功,后面的echo bar会执行。
  • e还有另一种写法o errexit
set -o errexit

六、set -o pipefail

set -e有一个例外情况,就是不适用于管道命令。
所谓管道命令,就是多个子命令通过管道运算符(|)组合成为一个大的命令。Bash 会把最后一个子命令的返回值,作为整个命令的返回值。也就是说,只要最后一个子命令不失败,管道命令总是会执行成功,因此它后面命令依然会执行,set -e就失效了。
请看下面这个例子。
#!/usr/bin/env bash set -e foo | echo a echo bar
执行结果如下。
$ bash script.sh a script.sh:行4: foo: 未找到命令 bar
上面代码中,foo是一个不存在的命令,但是foo | echo a这个管道命令会执行成功,导致后面的echo bar会继续执行。
set -o pipefail用来解决这种情况,只要一个子命令失败,整个管道命令就失败,脚本就会终止执行。
#!/usr/bin/env bash set -eo pipefail foo | echo a echo bar
运行后,结果如下。
$ bash script.sh a script.sh:行4: foo: 未找到命令
可以看到,echo bar没有执行。

七、总结

set命令的上面这四个参数,一般都放在一起使用。
# 写法一 set -euxo pipefail # 写法二 set -eux set -o pipefail
这两种写法建议放在所有 Bash 脚本的头部。
另一种办法是在执行 Bash 脚本的时候,从命令行传入这些参数。
$ bash -euxo pipefail script.sh

八、参考链接

(完)

系统启动时间

系统运行时间

记录用户操作历史命令history

查看占用cpu最高的进程

查看占用内存最高的进程

或者top (然后按下P,注意这里是大写) 其中第一句主要是为了获取标题(USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND)。 接下来的grep -v PID是将ps aux命令得到的标题去掉,即grep不包含PID这三个字母组合的行,再将其中结果使用sort排序。 sort -rn -k +3该命令中的-rn的r表示是结果倒序排列,n为以数值大小排序,而-k +3则是针对第3列的内容进行排序,再使用head命令获取默认前10行数据。(其中的|表示管道操作)
 
补充:内容解释 PID:进程的ID USER:进程所有者 PR:进程的优先级别,越小越优先被执行 NInice:值 VIRT:进程占用的虚拟内存 RES:进程占用的物理内存 SHR:进程使用的共享内存 S:进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数 %CPU:进程占用CPU的使用率 %MEM:进程使用的物理内存和总内存的百分比 TIME+:该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。 COMMAND:进程启动命令名称
 
 

查看当前网络带宽

linux View memory module information 查看内存条具体信息,几根内存条 命令 查看内存插槽及已插内存分布及大小命令

 

查看IO速率

关于更多的显示选项,可以在 iostat 的 man 手册中查询

观测进程的 I/O 性能指标: pidstat

磁盘性能指标 输出中每项的含义: 每秒读取的数据大小(kB_rd/s),单位是 KB; 每秒发出的写请求数据大小(kB_wr/s),单位是 KB; 每秒取消的写请求数据大小(kB_ccwr/s),单位是 KB。

根据 I/O 大小对进程排序: iotop

[root@host1 ~]# iotop 磁盘性能指标 前两行分别表示,进程的磁盘读写大小总数和磁盘真实的读写大小总数。因为缓存、缓冲区、I/O 合并等因素的影响,它们可能并不相等。 剩下的部分,则是从各个角度来分别表示进程的 I/O 情况,包括线程 ID

paste 合并文件行

View the server brand and model 查看服务器的品牌和型号

查看服务器序列号

循环遍历字符串列表

Excerpt
字符串列表或数组或元素序列可以通过在 bash 中使用 for 循环来迭代。本教程通过使用各种 bash 脚本示例展示了如何通过 for 循环迭代 Bash 中的字符串列表。如果您是 bash 编程新手,那么您可以在开始本教程之前阅读 BASH For Loop Examples 教程。


字符串列表或数组或元素序列可以通过在 bash 中使用 for 循环来迭代。本教程通过使用各种 bash 脚本示例展示了如何通过 for 循环迭代 Bash 中的字符串列表。如果您是 bash 编程新手,那么您可以在开始本教程之前阅读 BASH For Loop Examples 教程。

示例 1:在 for 循环中迭代由多个单词组成的字符串

创建一个名为“for\_list1.sh”的 bash 文件并添加以下脚本。带空格的字符串值在 for 循环中使用。默认情况下,字符串值以空格分隔。 For 循环会将字符串拆分为单词,并通过添加换行符来打印每个单词。
输出:

示例 2:使用 for 循环迭代字符串变量

创建一个名为“for\_list2.sh”的 bash 文件并添加以下脚本。将文本分配给变量 StringVal 并使用 for 循环读取该变量的值。此示例也将像前面的示例一样工作,并根据空格将变量的值划分为单词。
输出:

示例 3:迭代字符串值数组

创建一个名为“for\_list3.sh”的 bash 文件并添加以下脚本。在此脚本中使用类型声明了一个字符串值数组。数组中包含空格的两个值是“Linux Mint”和“Red Hat Linux”。该脚本将通过将这些值拆分为多个单词并作为单独的值打印来生成输出。但这不是正确的输出。此类问题的解决方案将在下一个示例中显示。
输出:

示例 4:将多个单词字符串值打印为单个值

创建一个名为“for\_list4.sh”的 bash 文件并添加以下脚本。在此示例中,数组变量 StringArray 的每个元素都包含两个单词的值。要在不拆分的情况下打印每个值并解决前面示例的问题,只需在 for 循环中用双引号将数组变量括起来即可。
输出:

示例 5:使用“\*”迭代数组的字符串值

使用以下代码创建名为“for\_list5.sh”的 bash 文件。这里,‘\*’符号用于读取数组的所有字符串值。第一个 for 循环用于在多行中显示数组值,第二个 for 循环用于在单行中显示数组值。
输出:
notion image

示例 6:迭代逗号分隔的字符串值

使用以下代码创建一个名为“for\_list6.sh”的新 bash 文件。这里,**逗号(,)**用于分割字符串值。 IFS 变量用于设置字段分隔符。
输出:

示例 7:一起读取多个字符串数组

创建一个名为“for\_list7.sh”的 bash 文件并添加以下脚本。在此示例中,定义了两个字符串数组并将其组合成另一个数组。外部for循环用于读取组合数组,内部for循环用于读取每个内部数组。
输出:

示例 8:使用模式读取字符串列表

使用以下代码创建一个名为 for\_list8.sh 的新 bash 文件。这里,**‘/, /’**模式用于根据逗号分割字符串值。
输出:
希望本教程的示例将帮助您了解如何使用 for 循环 来迭代字符串列表,有关此主题的视频,请参见以下内容:

🤗总结归纳

 

参考文章

 
 
致谢:
💡
有关Notion安装或者使用上的问题,欢迎您在底部评论区留言,一起交流~
 
 
Loading...