bash tips

shell的内容平时多多少少会使用到,说道底是经验积累问题,虽然每次总能找到最后的答案,但是总不能每次一用到就一句一句去搜索吧,还是按照场景整理在一起比较方便,先整理在这,然后持续更新。

用于“生存”的基本命令

这一部分最开始是整理在旧blog中的,现在整合在这里,以后就在这里进行继续更新了。

变量定义相关的

export

export可以将临时定义的变量定义成环境变量,比如在一个shell中临时定义的一个变量就没法在新打开的那个shell中继续再使用。使用export之后,这个变量就变成了环境变量,就可以在子进程中(新开的shell貌似不是子进程)再进行使用。具体的Linux中环境变量的分类可以参考之前linux env一篇

echo

用于显示环境变量 echo $变量名 可以显示出具体的变 量来

unset 变量名

这个用于取消刚才已经设置好的变量 unset之后 刚才已经定义好的变量就不在了

文件处理相关

cp

是在同一台linux上互相拷贝文件 而scp是在不同linux系统之间互相拷贝文件

scp

一种使用方式是将本地文件传输到远程服务器上,或者远程服务器上的文件传回到本地。认证时候的方式与ssh类似,参数格式是:

1
scp -i <私钥地址> 本地文件的路径 用户名@远程服务器的ip:远程服务器上的路径

另一种是进行端口映射,比如把本机上的一个端口映射到远程的服务器的某个服务上:

1
2
3
4
scp -p 4588 remote@www.abc.com:/usr/local/sin.sh /home/administrator
-v 用来显示具体的进度
-p 选择被占用的端口
-r 拷贝目录?

tar

可以用来压缩或者解压缩 具体的命令比较多 可以参考鸟叔p254
一般对tar.gz文件解压的时候 采用-xzvf参数 –x 表示使用解打包或解压缩的功能 –z表示通过gzip的方式进行解压 此时文件后缀最好是*.tar.gz –v表示在解压的过程中将处理的文件名显示出来 –f表示 filename后面接的是实际要进行处理的文件名
tar 用于打包的时候要这样使用
tar -czvf 打包之后所生成的文件名 需要打包的文件或目录
具体命令含义可以参考鸟叔p254
–z是打包成.tar.gz -j是打包成.tar.bz2

cp

复制文件或目录 cp [参数] 源文件 目标文件
重要参数 –a(相当于-pdr组合在了一起) 复制过去之后文件属性的参数也是一样的 默认情况 属性是不一样的

mv

移动文件或者重命名

rm

删除文件或目录
-f 强制删除 –r递归删除 –i产生交互的信息
注意删除文件的时候一定要谨慎使用-rf的参数

ssh

这个是使用security,shell远程登录其他的终端,关于ssh具体要了解的内容比较多,比如证书的原理以及地址,比如如何将本地端口映射到远程端口。具体可以参考之前一篇ssh tips。

查看磁盘的使用情况 处理空间不足的问题

df

查询目录的挂在情况 以及使用到的文件系统 以及基本的可用空间

du

查询到了哪个挂载的目录比较大的话,进入对应的那个挂载的目录,之后使用 du -ah –max-depth=1 . 可以查询当前目录下每一个子目录的大小。

进程相关

ps

查看当前进程 具体参数较多 常用的有
ps –af查看全部的进程并且以全格式的方式显示出来

service start/status/restart

service –status-all 这个命令可以列出全部的可以用供求service来使用的脚本
service命令可以使用的启动脚本或者服务 都要是在/etc/init.d文件夹下已经存在的?

pgrep

pgrep 是通过程序的名字来查询进程的工具,一般是用来判断程序是否正在运行
-l 列出程序名和进程ID;
-o 进程起始的ID;
-n 进程终止的ID;

od命令

这个命令可以用来查看某个文件中的详细信息,主要是可以根据不同的进制将每个字节的信息显示出来,文件内容可以通过管道的方式传过来,也可以直接跟在后面用od打开。
注意几个具体的参数,w是表示每列可显示的字符数,d 表示十进制 o 表示八进制(系统默认值)x 表示十六进制 n表示不打印位移值

网络相关

查看端口的占用情况

lsof -i tcp:port

lsof可以列出系统当前某个端口所打开的文件

nc –zv hostip 80

这个命令可以检查以hostip主机的80端口 看是否这个端口已经被打开
比如 nc –zv localhost 80
这个可以查看主机的80端口是否正常被打开

netstat

也可以查看网络相关的状态信息

重启网络服务(ubuntu)

sudo /etc/init.d/networking restart
service network-manager restart

terminal相关的快捷键

ctrl+D 用户注销 并且按两下会关闭terminal

ctrl+alt 弹出新的terminal(in ubuntu)

ctrl+shift+T 在同一个大的Terminal窗口中生成新的小的窗口 这样切换比较方便 看起来比较好

shift+ctrl+v 将剪贴板中的内容粘贴到terminal中

linux 的terminal中比较通用的几个快捷键(经常用到的)

ctrl+A 跳转到整行的前面

ctrl+E 跳转到整个命令行的末尾

ctrl+W 删除下一个分隔符之前的字符串

几种常用到的shell脚本

脚本写在一行

在使用docker或者使用k8s启动对应pod的时候,相应的启动参数通常都是一个string类型的数组。前两个参数通常是[“sh”,”-c”],第三个参数应该是所要执行的shell脚本,这个时候需要把平时看起来很长的脚本写到一行中。其实也很简单,只需要在换行的地方采用;隔开即可。具体的一些例子可以参考这个。

在k8s中注意args与command的使用与docker有区别,具体可以参考这个,仅仅通过docker方式启动的话,只需要使用args的参数而非command的参数。

判断某个文件/变量是否存在

-z可以判断这个变量的取值的长度是不是为0,如果存在,说明值的长度是不为0的。if [ ! -z “$JRE_HOME” ] 表示的是变量存在(内容长度不为0)。 类似的根据参数的不同可以判断文件夹(-d),文件(-f)是否存在。

1
2
3
4
5
6
7
8
#!/bin/bash
if [ ! -z "$JRE_HOME" ];then
JAVAFILE="$JRE_HOME"/bin/java
echo $JAVAFILE
echo "set JAVAFILE to JRE_HOME"
else
echo "not set JRE_HOME"
fi

关于while循环

直接参考这个(https://codingstandards.iteye.com/blog/780524)

常见的using case 是启动了一个程序之后 可能这个程序会创建一些shared file或者是shared dir 其他程序会读取这个shared file/dir的内容然后获取一些必要的信息,这个时候要确定这些文件已经存在了之后才能启动其他的程序。常用的方法是在shell脚本中设置一个while loop 不断地查看file或者dir,确定存在之后,再推出loop, 这里要注意的是,在while的command中,会使用空格的token来区分字符,但是在shell的赋值操作中,等于号要直接和变量连在一起

1
2
3
4
#check dir, if it is not exist, then create
if [ ! -d ./vtkdata ]; then
mkdir ./vtkdata;
fi
1
2
3
4
5
6
# check the dir, if it is not exist, then sleep and wait
while [ ! -d ./gs.bp ]
do
sleep 0.01
echo "dir not exist"
done
1
2
3
4
5
6
# check the file, if it is not exist, then sleep and wait
while [ ! -f ./<filename> ]
do
sleep 0.01
echo "file not exist"
done

让许多台机器使用ssh的方式进行通信

实际case是这样的,需要帮实验室的多台虚拟机搭建同样的测试环境,肯定是要脚本来执行了,先是在一台机器上把整个流程走通之后,再使用ssh的操作复制到其他的机器上来执行,按理说也不是很复杂的操作。

一般有两个脚本,第一个是使用ssh-key的方式,将本机生成的ssh-key的公钥放到其他机器上,ok之后,再将其他命令都用ssh的方式执行就不用用户名和密码了,用到了spawn与expect,整理类似的脚本还是比较折腾的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#set -e
# the minion
IP_LIST="centosma1-12345-root@10.10.102.94 centosma2-12345-root@10.10.102.95 centosmi1-12345-root@10.10.102.96 centosmi2-12345-root@10.10.102.97 centosmi3-12345-root@10.10.102.98 centosmi4-12345-root@10.10.102.99"
IFACE=eth0
# check th id_rsa.pub , create the new id_rsa if not exist
ssh-keygen -t rsa
for IP in ${IP_LIST}
do
#split the str by -
U=$(echo ${IP} |cut -d '-' -f 1)
P=$(echo ${IP} |cut -d '-' -f 2)
SSHIP=$(echo ${IP} |cut -d '-' -f 3)
C=${U}
MA=$(echo ${SSHIP} |cut -d '@' -f 2)
/usr/bin/expect<<-EOF
set timeout 3
spawn /usr/bin/ssh-copy-id -i /home/wangzhe/.ssh/id_rsa.pub ${SSHIP}
expect {
"yes/no"{send "yes\r";exp_continue}
"password:"{send "12345\r";exp_continue}
}
EOF
#ssh ${SSHIP} mkdir -p /home/vcap/kubeindocker/
#mkdir -p ./deploylog
#ssh ${SSHIP} setsid sudo yum update && sudo yum install docker-engine 1>./deploylog/${MA}-stdout.log 2>./deploylog/${MA}-stderr.log
done
#set +x

这个脚本主要是中间那段,在/bin/bash 脚本中插入一段/usr/bin/expect 脚本来执行交互,这里要注意的是EOF的使用结尾的那个EOF要放到正行的开头这里坑了好久,最后还是在同学的帮助下完成的。还要注意下expect对于多种情况时候的处理,以及exp_continue的使用。

最后通过setsid方式后台运行命令,并且将标准输入和输出记录到log中。

在后台静默运行某个程序

主要的有三种方式,采用sesid、nohup以及&,可以参考这个(https://www.ibm.com/developerworks/cn/linux/l-cn-nohup/)

比如下面这个例子,启动etcd并且把对应的日志信息输出到指定的文件夹。

1
2
3
$ setsid ./etcd -name infra0 -data-dir infra0 -cert-file=server.crt -key-file=server.key -advertise-client-urls=https://127.0.0.1:2379 -listen-client-urls=https://127.0.0.1:2379 1>stdout.log 2>stderr.log
如果采用&的方式
$ ./etcd --listen-peer-urls http://127.0.0.1:2381 --addr=127.0.0.1:4001 --bind-addr=0.0.0.0:4001 --data-dir=/var/etcd/data &> ./etcd.log &

有一次采用&的方式静默启动,但是应用不定时地就自动关闭了,也没有什么特殊日志,总是就是很奇怪,还是采用service的方式比较好,实在不行也得用个docker吧,要不应用挂了也不知道,所谓的进程监控了。

关于linux中的输出重定向的问题可以参考这个(https://www.jb51.net/article/64183.htm)

关于输入输出的重定向 1 2 的写法:

https://superuser.com/questions/436586/why-redirect-output-to-21-and-12

关于IO buffer

直接运行程序的时候,printf的结果可以输出到terminal中,但有时候使用了重定向之后,结果就无法输出,除非等程序运行结束。具体比如参考这个或者参考这个 主要是因为IO buffer的原因导致结果不会直接输出

一种方式是使用 expect 工具的 unbuffer command 作为临时调试使用,这个时候不管是重定向还是使用pipeline都可以work。或者是在输出的时候使用std::endl强制进行flush。

另外一种是使用比较完备的log工具,会直接将log信息记录写在打开的文件中而并非是通过标准输出。事实上参考这里可以看到 line-buffer 的概念,什么时候flush是个问题,频繁的flush会造成performance issue,但是一个flush信息也没有又回造成难以交互,总之是个tradeoff,有趣的是使用std::endl代替\n结尾可以强制地进行flush。

通过grep命令判断是否截取出相关的内容

首先是grep的那一套操作,如果不需要grep的内容输出,可以将输出结果重定向到null中。
之后通过if_exist=$?返回上一条命令,也就是grep那个命令的状态码,之后再用一个if操作来判断状态码是0还是1,如果是1的话,说明grep操作的确截取到了相关的内容,如果为0的话,说明grep命令并没有截取到相关的内容。注意有的时候ps命令与grep结合,希望搜索出特定的进程信息,这个时候容易吧grep本身的进程也显示出来,此时可以使用-v参数,在这个搜索命令的最后部分加上|grep -v grep 把含义grep关键字的内容从搜索结果中去掉。如果是filter out 其他的关键字 可以用类似的方式 比如 | grep -v "total" 会filter out 含有 total 的那一行

零碎的知识点

脚本中开头部分

一般比较常用的是写成 #!/bin/bash ,其中“#!”表示要位这个shell脚本指定一个解释器,当然后面还可以添加其他的内容,具体可以参考这个(https://blog.csdn.net/cjsycyl/article/details/7927727)

set使用

set -x 这个通常是于 set +x放在一起来使用的,开启调试模式。

1
2
3
set -x            # activate debugging from here
w
set +x # stop debugging from here

在”set -e”之后出现的代码,如果返回值是非0的,整个脚本就会立即退出,这个有时候会比较奇怪,有的时候可能脚本退出会引发一些奇怪的事情。一般命令正常就会返回0,命令异常就会返回其他的值。可以通过 echo $? 命令来查看上一条命令执行的状态码。

脚本中的空格问题

在给某个变量赋值的时候,等于号要和变量名紧挨在一起,不可以多一个空格出来,要不然系统会以为用户是在执行某个命令,这个实在是太容易错了,特别是对于像自己这样的新手而言。

单引号双引号反引号

网上相关的介绍内容较多。使用双引号,可引用除了字符$、`、\外的任意字符或字符串。用单引号括起来的特殊字符串将没有意义,最常见的是,使用了 ‘$varname’ 之后,$varname就会以字面值的形式出现。可以在双引号字符串中嵌套单引号。shell会将反引号中的内容作为一个系统命令来输出。

寻找固定后缀的文件

找固定后缀的文件
find [path] -name .pyc
递归查找
find [path] -name ‘.pyc’

希望将json以格式化的方式输出

输出格式比较好的json信息,需要用到python的-m参数,加载一个库之后将内容输出
echo ‘{“foo”: “lorem”, “bar”: “ipsum”}’ | python -m json.tool

脚本中传入参数

关于脚本中参数的传递问题
脚本名称叫test.sh 入参三个: 1 2 3
运行test.sh 1 2 3后
$*为”1 2 3”(一起被引号包住)
$@为”1” “2” “3”(分别被包住)
$#为3(参数数量)

更多传入参数的处理方式可以参考这里。

Here Document
Here document(https://blog.csdn.net/wangjunjun2008/article/details/24351045)相关的内容,希望将一些内容写入到某个文件中,执行类似的操作时,可以使用重定向。

字符串的拼接

关于字符串的拼接,直接 $command=string1’xxxx’ 之后再echo $command就是可以的。
或者$command=${var1}${var2}这样也是可以拼接起来的。

命令替换与变量替换
区别命令替换与变量替换 $()这种方式与${}这种方式的区别。具体可以参考shell十三问之8。

关于sed的使用

一般是用来替换一个配置文件中的某些内容
http://stackoverflow.com/questions/9366816/sed-unknown-option-to-s
注意:替换掉的字符串可能在其中含有”/“,这样在最后的串中就有多个“/”

变量分配时候的默认值
直接看别人整理的
http://unix.stackexchange.com/questions/122845/using-a-b-for-variable-assignment-in-scripts

其他参考资源

shell 十三问
http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=218853

这里找到了一个简体版
http://www.eefocus.com/haijiaoyouzi/blog/11-05/210795_9d1d9.html

这个分开整理的shell十三问,也比较好
https://blog.csdn.net/ancky_zhang/article/details/4583579

原帖
http://bbs.chinaunix.net/thread-218853-1-1.html

google的shell书写规范
https://zh-google-styleguide.readthedocs.io/en/latest/google-shell-styleguide/background/

bash编程基础总结
https://blog.csdn.net/marcky/article/details/7549513

推荐文章