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如何在多行匹配内容
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>
参数:
-PUse perl compatible regular expressions (PCRE).-zTreat 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-lit will display matches followed by a NUL char, see comments.-llist 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分隔文件
在数据分析的时候,希望将不同的数据拆分到不同的文件中。故可以使用下面的命令
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。(兜底方案)
疑问:为啥bash、sh都是用 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