shell学习总结
参考文献:
shell脚本最佳实践
前言
总想着写点shell
的学习总结,然而shell
知识点有点杂乱,自己没有掌握好,想写好就更难了,原因肯定是自己写少了。但是不记下来永远迈不开第一步,今天开个头,慢慢补。
潜规则
- 我没有进入娱乐圈,但是我知道那里面有很多潜规则需要遵循,不然没有发展的前途。其实写代码也是有潜规则的,如果不遵循码届的规则可能对于自己的发展也不是很好。至少遵循规则,在别的码农们眼里显得比较专业。
- 那
shell
的规则是啥呢? - 使用
shellcheck
来检查自己的脚本是比较不错的办法,安装比较简单。不要忘了也可以使用docker哦。编码能力比较强的也可以使用它来封装成一个web
服务,也是挺好的啊。
变量
变量的重要性不用多说,shell脚本的变量有哪些注意的呢?
- 变量不能使用
-
来链接,如current-path
就是错的变量。这个还真坑了我不少。想想真他妈的菜鸡,好像没有那个编程语言可以使用中划线声明变量吧。 - 声明变量为
local
只能在函数中使用。其他都是全局变量。注意是全局变量哦。 - envsubst很好的利用环境变量替换文件中的内容
- 变量赋值安全,如果脚本中一旦出现赋值空的字符串给变量,脚本就自动退出,可以参考set命令
- 使用默认的变量值
echo ${a-789} # 如果a没定义,则表达式采用默认值789
# 789
echo ${a:-789} # 如果a没定义或是空值,则表达式采用默认值789
# 789
echo $a
# a还是没有
echo ${a=789} # 如果a没定义,则表达式采用默认值,并给a赋值
# 789
echo $a
# 789 # a已经被赋值了
因此如何判断一个变量有没有声明呢?
[root@tom ~]# df=""
[root@tom ~]# echo ${df-33}
[root@tom ~]# if [[ ${df-x} != x${df} ]];then echo "已经被声明";fi
已经被声明
[root@tom ~]
字符串
shell的字符串太他妈的多了,都记住是在太难。
参考:https://www.cnblogs.com/cangqinglang/p/12498370.html
- 把字符串中的
src
全都替换成dest
$ var=wer123df123fff123
$ echo ${var//123/0000}
wer0000df0000fff0000
- 获取倒数
n
个字符
$ var=wer123df123fff123
$ echo ${var:0-4}
f123
$ echo ${var:0-4:2}
f1
需求:部门里面使用git
打tag
的方式记录版本号,最后三位是环境,前面的版本号,例如*****-123-rls
,表示的是release
的1.2.3
版本
使用shell
提取版本号
$ tag=lixi20201105-01-123-rls
$ echo ${tag:0-7:3}
123
常见需求
1. 获取脚本的路径
shell_path=$(cd $(dirname $0) && pwd)
2. 获取命令的执行结果给一个变量
$ path=$(pwd)
$ echo $path
/e/diary/LShell
$ path=`pwd`
$ echo $path
/e/diary/LShell
推荐,只要记 $()
3. 脚本中所有变量用"${var_anme}"
记住是双引号和{}。所以我的博客偷懒了。
4. 某系统中linux用户都会有一个lvm挂给这个用户,假设这样的用户的lv都是以linux_为前缀,请查处系统中所有这样用户的信息
for username in $(lvs | awk '{print $1}'| grep "linux_" | grep -v 'VG' | sed 's|linux_||');do id "${username }";done
5. BASH_REMATCH 简单的正则匹配使用bash自带的啦。
BASH_REMATCH 不用sed也可以
6. 脚本需要运行需要在不同的目录中进行,如何办呢?
# 1、通过参数让脚本运行在指定的目录
该方法比较不好,至少对我来说不好
# 2、cd 到指定目录,然后/path/to/script.sh,这样我就可以把脚本固定在某个目录下面,在任意目录执行该脚本了。如:
$ cd /d && /d/test1.sh
$ cat /d/test1.sh
#!/usr/bin/env bash
pwd
echo "$(pwd)"
7. set -xeuo pipefail
set -o nounset # set -u 试图使用未定义的变量,就立即退出。
set -o xtrace # set -x 在执行每一个命令之前把经过变量展开之后的命令打印出来。
set -o pipefail # 管道串起多条命令的情况下,只有最后一条命令失败时才会退出。
# 如果想让管道中任意一条命令失败就退出,就要用后面提到的-o pipefail 了。
set -o errexit # set -e 遇到一个命令失败(返回码非零)时,立即退出。 some_cmd || true
# 简写set -xeuo pipefail
8. 连续管道时,考虑使用 tee 将中间结果落盘,以便查问题
## cmd1 | tee out1.dat | cmd2 | tee out2.dat | cmd3 > out3.dat
$ cat test1.sh | grep set | tee 1.txt | grep "\-o" | tee 2.txt | grep "xtrace"
set -o xtrace # set -x 在执行每一个命令之前把经过变量展开之后的命令打印出来。
$ cat 1.txt
set -o nounset # set -u 试图使用未定义的变量,就立即退出。
set -o xtrace # set -x 在执行每一个命令之前把经过变量展开之后的命令打印出来。
set -o pipefail # 管道串起多条命令的情况下,只有最后一条命令失败时才会退出。
set -o errexit # set -e 遇到一个命令失败(返回码非零)时,立即退出。 some_cmd || true
# 简写set -xeuo pipefail
$ cat 2.txt
set -o nounset # set -u 试图使用未定义的变量,就立即退出。
set -o xtrace # set -x 在执行每一个命令之前把经过变量展开之后的命令打印出来。
set -o pipefail # 管道串起多条命令的情况下,只有最后一条命令失败时才会退出。
set -o errexit # set -e 遇到一个命令失败(返回码非零)时,立即退出。 some_cmd || true
9. md5sum检查文件的内容是否修改
#使用md5sum生成摘要文件
$ md5sum 1.txt > 1.md5
$ cat 1.md5
563868c9b2afd91f0bf03ac071e2b599 *1.txt
# 把文件和摘要文件放在一起
$ md5sum -c 1.md5
1.txt: OK
# 证明没有修改文件
# 增加一个换行
$ echo "" > 1.txt
# 校验不通过
$ md5sum -c 1.md5
1.txt: FAILED
md5sum: WARNING: 1 computed checksum did NOT match
10. find
(1)在当前目录下(包含子目录),查找所有txt文件并找出含有字符串"bin"的行
find ./ -name "*.txt" -exec grep "bin" {} \;
(2)在当前目录下(包含子目录),删除所有txt文件
find ./ -name "*.txt" -exec rm {} \;
11. bash -n test1.sh检查语法
$ bash -n test1.sh
$ vi test1.sh
$ bash -n test1.sh
test1.sh: line 13: unexpected EOF while looking for matching `"'
test1.sh: line 16: syntax error: unexpected end of file
12. 输出文件行号
cat -n file
nl -n ln file
13. yum
yum install --downloadonly --downloaddir=./ expect
14. iscsi存储
1.发现iscsi存储: iscsiadm -m discovery -t st -p ISCSI_IP:PORT
2.查看iscsi发现记录 iscsiadm -m node
3.登录iscsi存储 iscsiadm -m node -T LUN_NAME -p ISCSI_IP:PORT -l
4.登出iscsi存储 iscsiadm -m node -T LUN_NAME -p ISCSI_IP:PORT -u
## 不删除记录还会有iscsiadm -m node这个显示
5.删除iscsi发现记录 iscsiadm -m node -o delete -T LUN_NAME -p ISCSI_IP:PORT
15. docker的nextloud
docker run -d -p 80:80 -v /nextcloud:/var/www/html --name nextcloud --restart=on-failure:5 nextcloud:18.0.4
docker run -d -p 8000:80 -p 443:443 --name onlyoffice --restart=on-failure:5 custom-onlyoffice:v1.0.0
16. sed删除注释
#! /bin/bash
array_comment_lines=($(grep -n "^[[:space:]]\{0,\}#.*" ./application.yml | awk -F ":" '{print $1}'))
for line in ${array_comment_lines[@]}
do
echo "--"${line}"--"
sed -i "${line} s|^.*$||g" ./application.yml
done
#删除注释行 .*用于匹配#后面的任意字符
sed -i "/^[[:space:]]\{0,\}#.*/d" ./application.yml
#删除注释
sed -i "s/^[[:space:]]\{0,\}#.*//g" ./application.yml
sed '/^[ \t]*import/d' BeanUtil.java
#删除注释行
sed '/^[[:space:]]\{0,\}\/\//d' BeanUtil.java
#删除注释
sed 's/^[[:space:]]\{0,\}\/\/.*//g' BeanUtil.java
# 先单行
sed -i '/^[ \t]*\/\*.*\*\//d' BeanUtil.java
#在多行
sed -i '/^[ \t]*\/\*/,/.*\*\//d' BeanUtil.java
17. 转义执行
啥是转义执行呢?命令前加一个
主要是用来避免alias定义
比如你:alias ls=‘ls --color=auto’
使用\ls就不会打印颜色
18. asfasf
- afasf
- asf
数值运算
shell的数值运算真要命,不论记了多少回,几天过后一干二净。
# 所有变量默认是字符串,+ 之后是字符串
$ tom_age=23
$ jerry_age=22
$ total_age=$tom_age+$jerry_age
$ echo $total_age
23+22
# $[]
$ total_age=$[ $tom_age + $jerry_age ]
$ echo $total_age
45
#(())
$ total_age=$(( $tom_age + $jerry_age ))
$ echo $total_age
45
# let
$ let total_age=$tom_age+$jerry_age
$ echo $total_age
# expr
$ total_age=$(expr $tom_age + $jerry_age)
$ echo $total_age
45
推荐,只要记 $[]
$ echo $[ $tom_age * $jerry_age]
506
你真的理解ll显示的用户和用户组吗
$ ll
total 21
-rw-r--r-- 1 Administrator 197121 162 十一月 5 21:33 '~$ Microsoft Word 文档.docx'
-rwxr-xr-x 1 Administrator 197121 122 四月 25 2020 dirnameANDbasename.sh*
-rwxr-xr-x 1 Administrator 197121 335 四月 25 2020 Lgetopt.sh*
-rwxr-xr-x 1 Administrator 197121 90 四月 25 2020 Lshift.sh*
-rwxr-xr-x 1 Administrator 197121 54 四月 22 2020 Ltest.sh*
-rw-r--r-- 1 Administrator 197121 13604 四月 26 2020 '新建 Microsoft Word 文档.docx'
问:用户Administrator
和用户组197121
是什么关系?
答:没有关系。用户Administrator
可以是用户组197121
中的用户,也可以不是197121
的用户。
shell数组
采用面向对象的思想解说shell
的数组,类比于java
的ArrayList
、C++
的vector
。shell
的数组是动态的,大小不固定。数组元素类型不固定,可以是数值或者字符串
sed
$ sed -h
sed: unknown option -- h
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...
-n, --quiet, --silent
suppress automatic printing of pattern space
指定这个选项让sed只对要处理的输出到屏幕.默认情况下sed会将每行输出
--debug
annotate program execution
指定这个选项会对执行输出注释
-e script, --expression=script
add the script to the commands to be executed
如果你想对文本的每一行想做的不至一件事,可以用-e指定多个模式.当然也可以用;分开模式
-f script-file, --file=script-file
add the contents of script-file to the commands to be executed
模式太多写在命令行中麻烦,写在一个文件中,用-f指定文件位置就可以啦
--follow-symlinks
follow symlinks when processing in place
-i[SUFFIX], --in-place[=SUFFIX]
edit files in place (makes backup if SUFFIX supplied)
就地处理,就是真正修改文件.重要的option
-b, --binary
open files in binary mode (CR+LFs are not processed specially)
-l N, --line-length=N
specify the desired line-wrap length for the `l' command
--posix
disable all GNU extensions.
-E, -r, --regexp-extended
use extended regular expressions in the script
(for portability use POSIX -E).
重要!!! 使用扩展正则
-s, --separate
consider files as separate rather than as a single,
continuous long stream.
--sandbox
operate in sandbox mode (disable e/r/w commands).
-u, --unbuffered
load minimal amounts of data from the input files and flush
the output buffers more often
-z, --null-data
separate lines by NUL characters
--help display this help and exit
--version output version information and exit
If no -e, --expression, -f, or --file option is given, then the first
non-option argument is taken as the sed script to interpret. All
remaining arguments are names of input files; if no input files are
specified, then the standard input is read.
GNU sed home page: <https://www.gnu.org/software/sed/>.
General help using GNU software: <https://www.gnu.org/gethelp/>.
-e 允许多项编辑
-f 指定sed脚本文件名
-n 取消默认的输出
-i inplace,就地编辑
-r 或者 –E 支持扩展元字符
sed中的编辑命令:都是整行的命令
a:追加 向匹配行后面插入内容
c:更改 更改匹配行的内容
i:插入 向匹配行前插入内容
d:删除 删除匹配的内容
s:替换 替换掉匹配的内容
p:打印 打印出匹配的内容,通常与-n选项和用
=:用来打印被匹配的行的行号
n:读取下一行,遇到n时会自动跳入下一行
r,w:读和写编辑命令,r用于将内容读入文件,w用于将匹配内容写入到文件
------------------------以下为扩展-----------------------
h 清除保持空间的内容后,把模式空间里的内容复制到保持空间
H 把模式空间里的内容追加到保持空间
g 清除模式空间的内容后, 取出保持空间的内容,并复制到模式空间
G 取出保持空间的内容,追加在模式空间原有内容的后面
x 交换模式空间与保持空间的内容
sed '1~2d' 1.txt #从第一行开始删除,每隔2行就删掉一行,即删除奇数行
sed '2~2d' 1.txt #从第二行开始删除,每隔2行就删掉一行,即删除偶数行
Administrator@PC-20180827UNGA MINGW64 ~/Desktop
$ sed 's/123//g' 2.txt
abc
eee
def
456
Administrator@PC-20180827UNGA MINGW64 ~/Desktop
$ sed '/123/d' 2.txt
abc
def
456
sed 's/123/hello/' 1.txt #将文件中的123替换为hello,默认只替换每行第一个123
sed 's/123/hello/g' 1.txt #将文本中所有的123都替换为hello
sed 's/123/hello/2' 1.txt #将每行中第二个匹配的123替换为hello
$ sed 's/^[0-9]/(&)/' 2.txt #将每一行中行首的数字加上一个小括号 (^[0-9])表示行首是数字,&符号代表匹配的内容
abc
(1)23 123 eee
def
(4)56
(1)23
$ sed -r 's/(^[0-9])/(\1)/' 2.txt # -r或者-E表示使用扩展正则
abc
(1)23 123 eee
def
(4)56
(1)23
sed 's/$/& 'haha'/' 1.txt # 在1.txt文件的每一行后面加上"haha"字段
对一行模式多次:
1、用-e
2、在‘’中用;隔离模式
匹配多行,模式用,分割
sed '/123/,+1d' 1.txt #删除匹配123的行及其后面一行
sed '/123/,$d' 1.txt #删除从匹配123的行到最后一行
sed '1,/123/d' 1.txt #
webshell
有没有代码实现的webshell呢?可以在浏览器上打开网页??我找了好久,都没有我想要的。。。奈何我太菜。
docker的butterfly看着挺舒服的,docker run -d -p 57575:57575 garland/butterfly
如下:
我好像用java实现一个可以在网页打开的shell终端。如何实现。。。。websocket??
shell 冒泡排序
#!/bin/bash
function bubbleSort(){
local arr=($1)
local len=${#arr[@]}
for ((i=${len}; i>1; --i)){
for ((j=1; j < i; ++j)){
if [[ ${arr[${j} - 1]} -gt ${arr[${j}]} ]];then
local temp=${arr[${j} - 1]}
arr[${j} - 1]=${arr[${j}]}
arr[${j}]=${temp}
fi
}
}
echo "${arr[*]}"
}
regions=(9 1 4 3)
sd=$(bubbleSort "${regions[*]}")
echo ${sd[@]}
shell的script命令
这个命令还挺好用的
比如你想录制一下在命令行敲的所有命令和结果
那么
script res.txt
正常敲你的命令
ctrl + d
所有的命令和结果都会在res.txt文件中
shell的并行
肯定使用子shell了
# 子shell
sub_command &
#子shell的pid
sub_pid=$!
# 父shell做什么事
do_some
# 等待子shell完成
wait $sub_pid
組命令
{ echo RRR; ls; pwd; echo “ss”; } | sed ***