shell tips

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

用于“生存”的基本命令

变量定义相关

export

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

echo

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

unset 变量名

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

文件处理相关

cp

是在同一台linux上互相拷贝文件 而scp是在不同linux系统之间互相拷贝文件 复制文件或目录 cp [参数] 源文件 目标文件 重要参数 –a(相当于-pdr组合在了一起) 复制过去之后文件属性的参数也是一样的 默认情况 属性是不一样的

scp

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

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

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

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

mv

移动文件或者重命名

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

文件/关键字查找

grep

在当前目录下找某些关键的代码都在哪些地方被使用了

grep -r "<keyworkd>" ./

或者有的时候grep 与 cat 组合起来一起使用,比如查看log文件中的某些关键字:

cat <logfile> | <keyword>

strings

这个命令可以将二进制文件中的内容通过human readable的方式打印出来,比如有一次查看libstdc++.so.6中所支持的GLIBC的版本,就使用如下的方式进行操作:

strings ./libstdc++.so.6 | grep GLIBC

find
可以用来查找某个目录下是否包含指定的文件,比如以下的命令:

find ./ -name "db.json"

就是用来查看当前目录下是否含有 db.json 这个文件并且把相关的目录都打印出来

locate

locate 相当于是find -name的另一种写法,但是它的速度比find -name要快因为是从一个指定的db中进行搜索,这个指定的db(/var/lib/locatedb)会由系统进行定期的更新。

可以参考这个to get more detailed info.

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

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

其他

ssh

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

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脚本

每次要写一个shell sacripts就各种google各种查看以前资料,搞得很心烦,其实感觉shell能做的事情python都能做而且还能比较简单,于是能用python的情况下就尽量不用shell,这些内容还是先整理在这里省的时间又被白浪费。

脚本写在一行

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

在k8s中注意args与command的使用与docker有区别,具体可以参考这个(https://github.com/kubernetes/kubernetes/issues/8235),仅仅通过docker方式启动的话,只需要使用args的参数而非command的参数。

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

-z可以判断这个变量的取值的长度是不是为0,如果存在,说明值的长度是不为0的。if [ ! -z “$JRE_HOME” ] 表示的是变量存在(内容长度不为0)。 类似的根据参数的不同可以判断文件夹(-d),文件(-f)是否存在,注意每个字符中间的空格,否则shell在解析的时候容易发生错误。

#!/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

关于循环

直接参考这个

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

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

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

#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以及&,可以参考这个

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

$ 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中的输出重定向的问题可以参考这个

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

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

检查log中的关键字段

比如server会把文件写到log中,每个server process 可能需要完成一些load的操作 当所有的process把这些操作完成后,client才能启动,script需要不断检查log中的关键字段,看所有的scripts是否都启动完成,

SERVERNUM=8
result=0
while [ $result -ne $SERVERNUM ]
do
result=$(cat gsserver_mona_"$SERVERNUM".log | grep "Server running at" | wc -l)
echo "$result server loaded backend"
sleep 1
done

零碎的知识点

脚本中开头部分

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

set使用

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

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相关的内容,希望将一些内容写入到某个文件中,执行类似的操作时,可以使用重定向。

字符串的拼接

关于字符串的拼接,直接 $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书写规范
http://zh-google-styleguide.readthedocs.io/en/latest/google-shell-styleguide/background/

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

推荐文章