shell

shell是个很强大的工具,界面操作,用其来简单,但是想做到自动化,却有点困难。而且,会将限制用户的输入,这是好处,也是坏出。增加了限制,可以保证执行的有效性,但也限制了功能。

开发界面,也需要一定的成本。而直接开发出不带界面的,可能更容易一些。命令行直接再用强大的管道符号、重定向符号,能完成非常复杂的功能。

另外,*匹配也非常的强大,觉得这个功能,是能是bash解释器的功能。有了它,我们甚至省了循环。而且还有匹配(搜索)特定位置的文件的功能。

还有就是{}序列功能。

shellcheck

脚本语法检测工具,方便测试出脚本中存在的错误。

apt-get install shellcheck

bash快捷方式

Bash 快捷键
命令 说明
ctrl + a 移到命令行首
ctrl + e 移到命令行尾
ctrl + f 按字符右移
ctrl + b 按字符左移
ctrl + u 从光标处(不包含)删除至命令行首(包含)
ctrl + k 从光标处(不包含)删除至命令行尾(包含)
ctrl + w 从光标处(不包含)删除至单词字首(包含)
ctrl + d 删除光标处的字符
ctrl + h 删除光标前的字符
ctrl + l 清屏
ctrl + c 终止命令
c+f 左移一个单词
esc+b 右移一个单词
ctrl + 左右箭头 单词移动

shell综合使用例子

删除 none标记的镜像,综合用了 grep awk xargs 工具。

sudo docker image ls|grep none  |awk '{print $3}' | xargs -n 1  sudo docker rmi

更好的方式如下:

docker image ls | awk '$1=="<none>" && $2=="<none>"{printf "docker rmi %s\n",$3}'|sh

跟据指定的名称,删除相关的一组进程。

ps aux | grep test.php | grep -v grep |awk '{print $2};'|xargs -n 1 kill 

查看cpu使用的占用率,前10.

#cpu
ps aux| sort -nk3 |head 
# 内存
ps aux| sort -nk4 |head 

去重后统计

根据第4列去重,查看有多少条记录。注意,可能会有空行。

awk '{print $4}' /etl/dta/0.csv |sort -u|wc -l

统计某个次出现的次数

查询某个ip在日志文件中出现的次数。或者输出包含某个词的那一行。

sed -n '/165.22.31.3/p' /var/log/secure |wc -l

然后发现其实差不多等价于下面的内容(如果一行一个的话):

grep -c '165.22.31.3' /var/log/secure

统计某天csv文件的记录总数

csv文件每5分钟落一次盘,csv文件不包含header。

tee命令,将明细结果保存在额外的文件中。

ls /etl/dta/20200704/tb_scan_*.csv |xargs -n 1 wc -l | tee /tmp/20200704_check.txt |awk '{sum+=$1} END {print "Sum = ", sum}' 

查看某个文件夹内的文件总大小

ls -l|awk 'BEGIN{sum=0;}{sum+=$5}END{print sum;}'

awk统计

网上找到的各种统计。

1、求和

cat data|awk '{sum+=$1} END {print "Sum = ", sum}'

2、求平均值

cat data|awk '{sum+=$1} END {print "Average = ", sum/NR}'

3、求最大值

cat data|awk 'BEGIN {max = 0} {if ($1>max) max=$1 fi} END {print "Max=", max}'

4、求最小值(min的初始值设置一个超大数即可)

awk 'BEGIN {min = 1999999} {if ($1<min) min=$1 fi} END {print "Min=", min}'

字符长度

# 保存某个字段的长度、跟字段内容
awk -F ',' '{print length($67),$67}'   /path/to/my.csv > /tmp/error.csv
# 找出最长的几位
awk -F ',' '{print length($67),$67}'   /path/to/my.csv|sort -nr  | head ;
# 超过40,则输出该行的内容。
awk -F ',' 'length($67)>40{print $0}'  1.csv 2.csv 3.csv 

字段长度超24

貌似awk比较容易。其他的方式,不太好写正则

awk -F, 'length($2) > 24 || length($3) > 24 ' sd_td_mail_02_202107*
sed -n -r '/\,\w{25,}/p' sd_td_mail_02_202107* |head
grep 

打印具体某一行

# 注意 是两个等号。而不是 =
awk -F ',' 'NR==485383{print $1}'  /path/to/my.csv

sz发送未下载的文件

# 在已下载的机器上生成已下载的文件列表
ls *.flv > download.txt
# 在服务器上生成所有的文件列表
ls *.flv > all.txt
# 重命名已下载的文件
cat all.txt download.txt |sort |uniq -c |sort -n|awk '$1==1{print substr($0,8)}'  |xargs -n 1 -i mv {}  tototo{}
# 下载
sz tototo*.flv

查看某行文件的最大长度分布情况

sort -nr 按数字来排序,逆序。

head -n 1000 |tail 查看1000行左右的内容。替代方法 sed

awk -F ',' '{print length($8),$8}' /etl/dta/20200704/th_gpsdata_20200704144000.csv |sort -nr |head -n 1000 |tail

printf 命令

使用ls printf xargs 三者配合,达到生成特定的片段。

ls *.csv|xargs -n 1 printf "###%s###\n"

说明:printf只接受参数,而不接受标准的输入流。所以使用上面的方式。

导入到pg

使用命令,利用sed的编辑功能,生成特定格式的sql,然后执行导入功能。

# 先进入csv文件夹,然后执行
ls *.csv |sed "s:^:copy sandan_poi from '`pwd`/: "|sed "s:$:' with csv header;: " > /tmp/目标.sql
psql -h localhost -U gpadmin -d develop -f /tmp/目标.sql

ls 绝对路径

上面来源于这个例子

ls  |sed "s:^:`pwd`/: "

备注:由于替换一般使用 /分隔,但是路径中又多包含这个符号,为了避免歧义,使用:分隔,好像|也是可以的。

代码行数统计

感谢通配符号,使得统计代码,非常得方便。

ls *.php */*.php */*/*.php
wc -l  *.php */*.php */*/*.php   # 推荐使用
ls  *.php */*.php */*/*.php  |xargs -n 1 wc -l
cat  *.php */*.php */*/*.php  |wc -l
sed "/^\s*$/d" *.php */*.php */*/*.php  |wc -l   # 剔除空行

#  查找注释并打印输出
sed -n -e '/^\s*\//p;/^\s*\*/p;' Worker.php

# 输出空行、删除注释,再统计
sed -e '/^\s*$/d;/^\s*\//d;/^\s*\*/d;'  *.php */*.php */*/*.php  |wc -l 
# 说明  -e 表达式有3个,以;分隔   /正则/d
# /^\s*$/d;      删除空行, 
# /^\s*\//d;     \/ 转移,表示  / 字符。
# /^\s*\*/d;      \*  转移,表示 *字符 

代码行数统计(脚本)

使用脚本统计

for f in `ls *.php */*.php */*/*.php`;do
count=`sed -e '/^\s*$/d;/^\s*\//d;/^\s*\*/d;' $f|wc -l`;
#echo "$count   $f"
printf "% 4d  %s\n" $count $f
done

字符串排序

因为无法直接在echo中 输出换行符号,故用 sed 来替换成换行符号。

echo -e 参数支持转义符号。

echo 'b c a' |sed 's/\s/\n/g'|sort
echo -e "b\nc\na"|sort

awk拆分

原始的数据结构如下:

大末端超市|98091419,98091419,98091419|app1,app1,app1,app1,app1,app1|market|980090|韵达超市-刘达达

拆分的脚本如下:

# 更新部门信息
echo group_cn,group_en,leader_name,leader_number > import_group.csv
awk -F '|' '{print $1","$4","$6","$5}' basic-data-20210601_sort.csv >> import_group.csv

# 更新人员的对应关系
echo group_en,number > department_number.csv
awk -F '|' '{print $4","$2}'  basic-data-20210601_sort.csv| awk -F ',' '{for (i = 2; i <= NF; i++) print $1","$i}' >> department_number.csv

# 更新关联app
echo group_en,deploy > department_deploy.csv
awk -F '|' '{print $4","$3}'  basic-data-20210601_sort.csv| awk -F ',' '{for (i = 2; i <= NF; i++) print $1","$i}' >> department_deploy.csv

swagger operationId

uniq -c 能查看重复的数字

如果返回的结果太多,还能近一步用 grep -v 来排查(注意正则),或者awk $1 > 1.

grep operationId qingcloud-api.swagger.yaml |sort | uniq -c

# 太多的时候使用
grep operationId qingcloud-api.swagger.yaml |sort | uniq -c |grep '^\s+[^1] '

随机密码

tr是转换工具。下面的亮点在于这种命令格式tr < |,从来没有遇到过这样的管道组合。故记录下来。

tr -dc A-Za-z0-9_ </dev/urandom  | head -c 8 | xargs

批量求md5sum

只求一个文件的md5比较简单,关键是要求批量。原来很多命令,都可以直接执行。

这个时候利用上xargs,比较方便。

# 更简单的方式
ls *.jpg |xargs md5sum   > ../s.txt
# 其实更方便的方式如下(弊端好像也有,文件不能太多太多):
md5sum *.jpg > s2.txt

脚本的方式:

#!/bin/bash

if [ -z $1 ] ;then
    echo "Usage: $0 targetpath,$0 my/path"
    exit
fi

count=$(ls $1|wc -l)
i=1
for file in $(ls $1);do
    echo "$i/$count $file"
    md5sum "$1/$file" >> md5.txt
    i=$[i+1]
done

挑选照片

按名称挑选照片

md5sum *.jpg > ../pic.txt

cat pic.txt |awk -F'*' {'print $2'}|xargs printf "cp vivo_z3/DCIM/Camera/%s select\n" |sh

下载照片

pic文件存的是下载图片的路径的路径。文件按序号存储。还功能指定下载指定序号的图片。

注意47前面加了空格,是表示想搜索除 url外的序号。否则,可能序号、url都搜索。

# 下载全部
cat -n pic.txt |awk '{print "curl -o "$1".png "$2}' |sh

# 下载指定行的图片
cat -n pic.txt |grep " 47" | awk '{print "curl -o "$1".png "$2}' |sh
# 其实指定行,用 sed  更好
cat -n pic.txt |sed -n '47p' | awk '{print "curl -o "$1".png "$2}' |sh

下载m3u

# 下载
sed -n '/^http/p' index.m3u8 |awk '{printf "curl -o %s %s \n",NR,$1 ;}' |sh

# 合并文件 注意,直接cat * > ../down.ts 不行,因为顺序不正确
for i in $(seq 1 $(ls |wc -l));do cat $i  >> down.ts ;done 

其他命令

# 进度
ls |sort -n |tail
# 检查文件  查看最小的文件
ls -Slhr |head
# 指定行的下载格式 注意空格是用来匹配的
sed -n '/^http/p' index.m3u8 |awk '{printf "curl -o %s %s \n",NR,$1 ;}'|grep "o 6 "

统计不包含关键字的文件

查找关键字:AccessControl::className,避免自己每个文件手动打开,检查。故:

其他方法。直接 grep “AccessControl::className” -R . 这个只能查找到有的文件。

for each in `ls `;do grep "AccessControl::className" $each > /dev/null ; if [ $? -ne 0 ]; then echo $each; fi; done

xargs与awk、sed

假设在/md目录下执行操作。

find .  -name '*.md' |xargs  -n 1 -i echo {} 模板
find .  -name '*.md' |awk '{printf "%s 模板\n",$1}'    # 函数的参数之间,用逗号分隔
# sed 略  反正也是修改,替换
# 或者将输出写到文件   然后for循环

有的时候,需要加上 |sh,来直接执行命令。或者保存到文件,先看下输出是什么样子的。

awk  ...  |sh

统计字段

拿到每个csv文件的header,然后是统计有多少个字段。由于使用了*,head中有文件名称。故使用sed,删除指定的行。

以下验证每个字段,都是169个字段。

head -1 /addr7/full_piece/real/202106*/*00.csv |awk -F',' '{print NF}' |sed '/^0$/d;/^169$/d;/^1$/d'

突然发现,也可以sort -u.

head -1 /addr7/full_piece/real/202104*/*00.csv |awk -F',' '{print NF}'|sort -u

排序

文本处理的结果如下:

sz Linux三剑客超全超详情教程(grep、sed、awk入门到精通有这一套足够了) (P1. 01-shell编程-debug).flv
sz Linux三剑客超全超详情教程(grep、sed、awk入门到精通有这一套足够了) (P2. 02-shell编程-总结).flv
sz Linux三剑客超全超详情教程(grep、sed、awk入门到精通有这一套足够了) (P3. 03-三剑客正则-正则什么鬼).flv
sz Linux三剑客超全超详情教程(grep、sed、awk入门到精通有这一套足够了) (P4. 04-三剑客正则-正则vs通配符).flv
sz Linux三剑客超全超详情教程(grep、sed、awk入门到精通有这一套足够了) (P5. 05-三剑客正则-基础正则01).flv

思路:sed -r来开启正则功能,将要匹配的内容,用括号括起来。\1,引用第一个匹配的内容。\0,匹配该行。然后转换成数字来排序。考虑到要处理的内容中有空格,故换成了---分隔。

ls *.flv |sed -r 's#^.*P(.*)\. .*$#\1---\0#' |sort -n |awk -F'---' '{print "sz "$2}' 

突然发现:

因文件名中包含了空格啥的,我用*来匹配。则出现如下方式。直接按数字循环,即可。也不用排序了。

ls *20*.flv
sz *20*.flv

seq 1 20 |awk '{print "ls *"$0"*.flv"}'|sh  -- 尽量避免出现重复的

统计文件个数

# 统计月的文件夹
for each in `seq 1 31`;do ls /addr7/full_piece/real/202105`printf '%02d' $each`/*00.csv|wc -l;done

# 
for each in `seq 1 31`;do 
	path=/addr7/full_piece/real/202105`printf '%02d' $each`/*00.csv ;
	echo $path;  # 没有想到,居然是一批目录,通配符号已经被解释了
	ls $path|wc -l ;
done

csearch提取函数

 csearch "create or replace function" |awk -F ':' '{print $2}' |sort -u |sed 's/^/c:/;s#\#/#g' > functions.txt
for each in `cat functions.txt`; do  sed -n '/create or replace function/,/language/p' $each >> plpgsql.txt ;done
# 每个language 后面增加空行的
sed -i '/language pl/a \\n\n\n'  plpgsql.txt
# 上面的为啥是 \\n  因为\\n输出的是 回车
# 如果是  \n  结果就会输出一个 换行 n 字母。  貌似是吞了一个 \

网络状态统计

time_await较高,然后自己搜到如下命令。

https://zhuanlan.zhihu.com/p/45102654

netstat -an | awk '/^tcp/ {++State[$NF]}END{for(key in State)print key "\t" State[key]}'

转换为csv

原理很简单,就是替换分隔,遇到双引号,转换成两个双引号。(可能因为要转义,显得难懂一些)

$1 = $1 为啥要加这一句,我还不懂,但是两者差别很大。测试过,只要 数字不超过本身的长度即可,超过了,则在末尾多出很多多余的列。如有3列 ,$2 = $2,$3 = $3,也是可以的。

awk 'BEGIN{FS="\t";OFS="\",\""}{gsub(/"/,"\"\""); $1 = $1;printf "\"%s\"\n",$0}'
awk '{a[$1]+=$3}END{for(i in a)print a[i],i}' vm_cpu_cores.txt |sort -n

csv统计之坑

下面命令,csv统计行数有非常大的出入。

。首先uniq -u的含义,并不是去重,而是只打印唯一行,即凡是重复的就不统计。另外,最好不要使用shell来统计行数,因为字段里面可能会有\n等符号。

find . -name "tu*.csv" | xargs cat | cut -d , -f 1 | sort |uniq -u | wc -l

fork炸弹

非常简短的代码,能实现递归的fork,几秒之类,系统就能被资源耗尽,无响应。

冒号即为函数名称。通过管道,再形成调用。

:() { :|:& };:

查看附近几行

查找特定的内容,并想输出附近几行,grep -n (n为数字)非常的好用。

尝试用sed命令,但是seq 1 10|sed -n '/2/~6p'seq 1 10|sed -n '/2/~/3/p',这种波浪号语法不支持,只支持逗号,这种。

grep -3 "string_agg" /yd/td2/runtime/cron/log.txt

扩展

grep -5 'parttern' inputfile //打印匹配行的前后5行
grep -C 5 'parttern' inputfile //打印匹配行的前后5行
grep -A 5 'parttern' inputfile //打印匹配行的后5行
grep -B 5 'parttern' inputfile //打印匹配行的前5行

-- 查找到行好,配合  sed -n '1,2p' 这中语法,来查看细节

统计导入总数

sed 虽然也能匹配到,但是,用grep分来步来做,更直接一些。

cat -n log.txt| grep 打标 |tail -2
# 每行的数据是这样的  COPY 4167873
sed -n '215611,215807p' log.txt |grep COPY |awk 'BEGIN{sum=0}{sum+=$2}END{print sum}'

wget提交form表单

wget --post-data="question=怎么了&user=zhenghongxi"  
wget --post-data=\"question=怎么了\&user=zhenghongxi\"  

代码模块/写文件

遇到动态写shell到文件,有的时候会用cat。下面是另外一种方式,用大花括号包裹起来的,作为一个整体,相当于会运行一个子shell进程,输出的命令写到文件里面。

借鉴。

{
    echo '#!/bin/sh';
    echo 'set -e';
    echo;
    echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"';
} > test.sh
&& chmod +x test.sh

以前用到的方式:

# 后台运行
{
    # 多个命令
} &

yii2快速导入模块

快速的生成指定模块下的use 函数。

ls */models/*.php |sed 's#/#\\#g;s/.php$/;/g;s/^/use /'


# 查找函数
grep function common/models/DoubleLiving.php 

{}秒用

比如想在特定的路径下搜索代码,非常的方便。

grep 'api/v1/namespaces' */{contro*,model*}/*.php
#或者
grep 'api/v1/namespaces' */{contro,model}*/*.php

或者备份,

cp config.php{,.bak}

统计代码

grep  -oh 'array_\w*' vendor/yiisoft/yii2/*/*.php |sort |uniq
  • -h 隐藏文件名称
  • -o 只显示匹配到的内容

统计当前目录下的文件格式

下面的小括号,可要可不要,结果一样。

(for each in *.*;do echo ${each##*.};done)|sort|uniq -c |sort -rn

# 貌似还有一点问题
(for each in `find . -type f`;do echo ${each##*.};done)|sort|uniq -c |sort -rn

sed !d

反向操作,666

sed -e '/abc/,/efg/!d' 
# 等价于  sed -n '/abc/,/efg/p' file

seq 1 10 |sed -e '/2/,/5/!d'

sed 条件替换

同理,还有条件删除、条件添加,等等。

# /class/ 正则匹配到该条件,才会触发 s 替换
sed -i '/class/s/Demo/ThreadDemo/' snippet/java/ThreadDemo.java

grep如何在多行匹配内容

grep

  • if ‘abc’ and ‘efg’ can be on the same line:

    grep -zl 'abc.*efg' <your list of files>
  • if ‘abc’ and ‘efg’ must be on different lines:

    grep -Pzl '(?s)abc.*\n.*efg' <your list of files>

参数:

  • -P Use perl compatible regular expressions (PCRE).
  • -z Treat the input as a set of lines, each terminated by a zero byte instead of a newline. i.e. grep treats the input as a one big line. Note that if you don’t use -l it will display matches followed by a NUL char, see comments.
  • -l list matching filenames only.
  • (?s) activate PCRE_DOTALL, which means that ‘.’ finds any character or newline.

for in 多行的困扰

空格分隔,在for里面会当成一个整体。故,可以用sed 、tr等命令,进行转换。

# 当成整体,结果只输出一行
for each in '1 2 3 4';do echo $each;done

# 转换多行
echo '1 2 3 4' |tr '[ ]' '\n')

# 再搭配for使用
for each in $(echo '1 2 3 4' |tr '[ ]' '\n');do echo $each;done


mytest='
1
2
3
4
'
# echo $mytest 跟 echo "$mytest"  显示不一样,后者原样输出
# 按下面即可
for each in "$mytest";do echo $each;done

for + 管道

处理多行数据,我一般习惯用 awk,其实,for,也很好。

备注:(done后面不能加分号,为啥?因为,写其他命令的时候,也没有加啊,sort 后面加;肯定也报错),另外,while也是可以的

for each in `ls **/*.jpg`;do echo ${each%/*};done |sort |uniq

while read + 管道

直接将变量读入到each 中,然后进行处理。

seq 100|while read each;do  echo $each ;echo '####';done
# 再接输出
seq 100|while read each;do  echo $each ;echo '####';done| sed 's/#/bb/g'

对比for+管道,whileread更灵活。for貌似必须要放在最开始,参数,必须要先放到in后面的参数里面。

awk分隔文件

参考1 参考2

在数据分析的时候,希望将不同的数据拆分到不同的文件中。故可以使用下面的命令

seq 1 10 |awk '{print $0 >> $1.txt}'

# 字符串处理  注意,字符串拼接,直接使用双引号即可
# 没有right函数,使用 如下计算
fn=substr($1,length($1),1)".txt"

seq 1 100 |awk '{fn=substr($1,length($1),1)".txt";print $0 >> fn}'

文件拆分如下:

貌似效率并不是太高( >> 会频繁的打开、关闭文件吗????)

awk '{fn=substr($4,length($4)-4,4)".csv";print $0 >> fn}'   /etl/*/tb_*.csv

100G文件,大概10分钟

awk去重

貌似也能按某列,去重。

awk '!a[$0]++' seq.txt 

xargs 并发

xargs -P 并发数

bin/sh

好像是在一个容器内看到如下的代码:

/bin/sh -c TERM=xterm-256color; export TERM; [ -x /bin/bash ] && ([ -x /usr/bin/script ] && /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) || exec /bin/sh

代码大概的含义是:

/bin/bash如果存在,则优先运行/usr/bin/script,否则运行/bin/bash

不存在时,则运行 /bin/sh。(兜底方案)

疑问:为啥bashsh都是用 exec来运行呢?

json取巧处理

懒得调用其他的脚本语言来处理了,直接使用很长的省略号,来匹配固定字数的字符。比如下面,是为了获取订单号。配合awk取出数字。

curl  -s --location --request GET "$API_HOST/v1/warning_order/list?&limit=&status=1" \
--header "token: $TOKEN" \
--header 'appId: w4vr9fowiwgjwyb8' \
--header 'User-Agent: apifox/1.0.0 (https://www.apifox.cn)' |grep -o '"order_id":".................................'  |awk -F'"'  '{print $4}'  > undo_order_list.txt

如果,上面的步骤,还需要在下一步处理,则可以,导出到文件,或者定义成一个函数。或者,懒的话,直接丢到$()等语法块,还可以配合管道,接上while read each等方式。

也可以使用下面的方式,来匹配整

curl ..... | grep -o '"order_id":"[^"]*'|grep -o [0-9]*

grep直接正则匹配

grep参数-P 使用perl的正则匹配模式。

wget -O- https://zenodo.org/record/5092942 | grep -oP 'https://zenodo.org/record/5092942/files/flightlist_\d+_\d+\.csv\.gz' | xargs wget

ps查看内存占用情况

%cpu进程的cpu占用率%mem进程的内存占用率vsz进程所使用的虚存的大小

Rss进程使用的驻留集大小或者是实际内存的大小

TTY与进程关联的终端(TTY)

STAT检查的状态:进程状态使用字符表示的,如R(运行正在运行或准备运行)、S(休眠睡眠)、I(空闲空闲)、Z(僵死)、D(不可中断的睡眠,通常是I/O))、P(等待交换页)、W(换出,表示当前页面不在内存)、N(低优先级任务))T(终止)、W没有驻留页面)

START(进程启动时间和日期)

TIME;(进程使用的总cpu时间)

ps aux |grep php  |awk '{print $6/1024 "M" "\t" $0}'

显示进程是什么时候启动的,lstart

ps -eo pid,lstart,etime,cmd |grep php

k8s获取集群pod状态

kubectl get pods -A |awk 'NR>1{arr[$4]+=1}END{for(n in arr){printf "%30-s\t%5d\n" ,n,arr[n]}}'

查看网络连接

# 查看网络连接
ss -t4n state time-wait sport = :80

# 使用awk 来统计
ss -t4n state time-wait sport = :80 |awk '{split($4,ip);s[ip[1]]++} END{for (i in s ){ if (s[i]>10)print i,s[i]}}'

# 加入防火墙
echo iptables -I INPUT -s $ip -j DROP

awk多行为单位,统计

使用ansible统计机器的cpu等信息。亮点在于:多行内容,提取感兴趣的内容。


ansible all -m shell -a "cat /proc/cpuinfo|grep proce|wc -l" |tee log_cpu.txt

cat log_cpu.txt  |awk 'NR%2==1{printf $1 "\t"}NR%2==0{printf $0 "\n"}' > cpu.txt
# 查看内存
ansible all -m shell -a "free -h |grep Mem"|awk 'NR%2==1{printf $1 "\t"}NR%2==0{printf $2 "\n"}'

exec

有个很疑惑的问题,为啥要使用 exec这个命令呢? 直接使用vim不就可以了?

exec vim "$@"

sed + grep 只替换指定文件

参考文章

这应该是一种常用的套路。像sed、mv、wc等等命令都可以接n多个文件名。而这些文件名通过计算而来,比较方便。grep命令,-r代表递归查找当前文件夹,而-l参数,只显示文件名,而不显示具体查找出的内容(文件名只会出现一次)。

sed -n "s?](.*|pic|?](http://xxx.clouddn.com|pic|?gp" `grep "|pic|" -rl ./`

js资源版本号替换

主要的难点:搞清楚,sed里面的\d不能用,只能使用[0-9],另外,记得双引号内的变量才会解析。


VERSION=`date '+%Y%m%d%H%M%S'`

sed -i "s/ver=[0-9]*/ver=$VERSION/g"  frontend/web/safe_list/*.html
sed -i "s/ver=[0-9]*/ver=$VERSION/g"  frontend/web/safe_list/html/*.html

找不到指定内容,则输出文件名称

一开始的想法是,先grep后获取文件命令,再对比,那些文件名不在列表中。感觉该思路略麻烦,遂直接采用下面的方式。

for 直接用来遍历文件夹。 grep的输出结果直接丢弃。并根据是否成功,采取进一步的操作。

for each in backend/controllers/*.php;do
    grep 'AccessControl' $each >/dev/null ;
    if [ $? -ne 0 ];then 
        echo $each;
    fi;
done

if-else

属于低配版的If else。当然,如果无论测试结果是否为true都执行,则更改为顺序语句即可。即cmd1;cmd2;cmd3

[ 'yes' == 'yes' ] && echo 1 || echo 2
# 甚至嵌入到其他表达式中。``  或  $( 命令 )
RESULT=$([ 'yes' == 'yes' ] && echo 1 || echo 2)

$CMD

适用于,先展示要执行的命令,后执行。

#先展开,后执行
CMD="echo 1"
$CMD  


# 也可以,但是会作为一个整体,即不能加任何参数
CMD="ls"
# CMD="ls -alh" # 会作为一个整体识别,而出错。
"$CMD"


# 替代方法
CMD="ls -alh"
echo $CMD |sh
echo "$CMD" |sh # 暂未发现差别

gum

[ $(gum choose yes no) == "yes" ] && echo yes || echo no
gum confirm 'continue ,now?'  && echo yes || echo no

# 从指定文件中选择一个执行
find . -type f -name "*.sh" -exec grep "^docker"  {} \; |grep run |sort -u |gum filter

统计每个文件夹下的文件数量

关注点:遍历文件,要防止文件名中有空格,故最好采用 in *.flv这种格式,其返回的字符串,如果有空格,应该会包裹上引号,防止被空格拆分。而 ls后面的,也是一样的,继续包裹保护。

for i in *.flac;do  echo $i    `ls "$i/"*".flac"|wc -l`;done

拷贝目录

find . -type d -exec mkdir -p "/e/教程/"{} \;

# 清理空目录
 find . -type d -empty -exec rmdir {} \;

xargs并发请求示例

参数 -P 指定并发的进程数。

-i 可以使用大括号,来表示要引用到的参数。

-n 表示每次只消耗n个参数。

awk '{print $1}' /var/log/nginx/access.log |sort -u |xargs -n 1 -P 3 -i python ~/ip_location.py {}

awk之执行命令system

使用system函数,来调用系统的命令,从而能调用其他任何的命令。需要注意的是,system("这里面参数不会被解析"),一般紧挨着双引号外面放即可。

顺便讲一下,为啥awk命令参数,都用'{print ...}'这种单引号包裹,这是为了保护里面的$1这些变量,这些变量实际并不需要被外面的bash来带入进去,使用了$变量,而又没有保护的话,那么肯定会报错。

那么,如果我真的需要外面的参数怎么办?放在引号外,即可。它在shell里面,即为字符串的拼接功能。

awk '{print $1}' /var/log/nginx/access.log |sort|uniq -c |sort -rn |awk '{printf $1"\t";system("python3 /home/chaofml/git/snippet/python/ip_location.py "$2)}'

查找md文件中的引入链接

核心点是,开启grep的perl模式。

grep --help|grep regexp
  -E, --extended-regexp     PATTERN is an extended regular expression (ERE)
  -G, --basic-regexp        PATTERN is a basic regular expression (BRE)
  -P, --perl-regexp         PATTERN is a Perl regular expression
  -e, --regexp=PATTERN      use PATTERN for matching
  -w, --word-regexp         force PATTERN to match only whole words
  -x, --line-regexp         force PATTERN to match only whole lines

正则部分讲解

https?://.*?             # 想要匹配的内容
\(https?://.*?\)         # 带上边界条件,但又不是我想要的
\(       \)              # 括号,本身也是正则内的语法,要用到转义
(?<=\()                  # 反向预测 
(?=\))                   # 正向预测
(?<=\()https?://.*?(?=\))# 最终结果

4

find . -maxdepth 4 -name "*.md"  -exec grep -oP '(?<=\()https?://.*?(?=\))' {} \;

split

使用cat * 来合并。


find . -maxdepth 1  -type f  -size +100M -print;

find . -maxdepth 1  -type f  -size +100M -exec split -b 80M {} {}. \;
find . -maxdepth 1  -type f  -size +100M -exec rm -f {} \;

bash *.sh

为啥不能直接使用bash *.sh这种方式呢?这是因为bash test.sh 参数1 参数2,其他都是作为参数传入的。

cat *.sh |bash |tee log.txt

ab压测

注意下,seq命令格式

# seq 起 步长 终
for i in `seq 100 100 2000`;do echo $i; ab -n 100000 -c $i -k http://10.172.41.206:8088/index.html 2>&1 |grep 'Requests per second:'  ;done

watch 监控多条命令

多条指令,整体用引号包裹。注意,如果内部有引号,可能需要转义。

watch "ls page* |wc -l ;ls -lht page*|head -2; ls detail* |wc -l ;ls -lht detail*|head -2;"

scp *.sh


scp `find . -type f -mmin -480 |grep -v '.git'` jobvm:/home/chaofml/git/it-monitor-code


# atime  -n_day   
# mtime  -n_day 

# amin  -n_min

rm find

# 配合 ctrl+a  ctrl+e 也非常方便
# 相当于  find -exec rm {} \;
# 如果有for each in ``;do done,则略微麻烦一些
rm `find . -mmin 60`

对比重复文件

简单的根据文件名,来查找文件是否重复。

for each in *.pdf;do  echo $each;find .. -type f -name "$each" -exec md5sum {} \;;done

mybatis简单分析

最好使用xml来解析,下面只是一种简单的方式。

find ./main/resources/mybatis  -type f  -iname "*.xml"  -exec cat -n {} \; > mybatis-all.xml
grep '<delete ' mybatis-all.xml
grep '<delete ' mybatis-all.xml | wc -l


sed -n '/<delete /,/<\/delete/p;' mybatis-all.xml
sed -n '/<select /,/<\/select/p;' mybatis-all.xml
sed -n '/<update /,/<\/update/p;' mybatis-all.xml


# 循环
for i in insert update select delete;do echo $i;sed -n "/<$i /,/<\/$i/p;" mybatis-all.xml > mybatis-"$i".xml; done
for i in insert update select delete;do echo $i;grep "<$i " mybatis-all.xml | wc -l ;done

# 查看占比
wc -l mybatis-*

# 统计使用的标签
grep -Po '<[a-zA-Z]+' mybatis-all.xml  |sort |uniq -c

spring pom依赖

快速查看项目的依赖

find . -type f -name "pom.xml" -exec cat {} \;|grep 'artifactId'

# 方式2
shopt -s globstar
grep 'artifactId' **/pom.xml