[TOC]
一、这里都是一个个独立的小例子
1、遇到一个诡异的问题
目标:将文件名后缀去掉,然后在上层目录下,建立一个同名目录,然后将.zip压缩包的内容全部解压过去
# echo 文件名.zip | sed -e 's/\.zip$//' 这是将后缀名去掉
# echo 文件名.zip | sed -e 's/\.zip$//' | xargs -i mkdir ../unzipPath/{}
此时试图在上层目录创建一个文件夹,但是{}的内容为空,将其打印出来显示也为空
# echo 文件名.zip | sed -e 's/\.zip$//' | xargs -i echo ../unzipPath/{}
==>>试图变通,未果:
# echo 文件名.zip | sed -e 's/\.zip$//' | xargs -i mkdir -p {} && mv {} ../unzipPath/
继续尝试,将 xargs -i mkdir ../unzipPath/{} 改为 xargs -i mkdir ‘../unzipPath/{}’,则在目标文件夹发现,新增了一个目录,名称为随机乱码
但是 xargs -i mkdir ‘../unzipPath/{}’ 显示的却是预期的文件名
再次尝试: xargs -i mkdir ../unzipPath/\{\} 同样增加了一个乱码目录名,而 xargs -i echo ../unzipPath/\{\} 却是预期的文件名
第二个困惑,大目录查询报错问题,不知道如何解决
对查询全部项目中的文件,搜索关键字
grep -rni keyWords `find ../../ -name *.c`
这种格式很漂亮,能找出对应C文件,在哪行有关键字。
但是当目录下C文件太多时,会报错:
-sh: /usr/bin/grep: Arguments list too long
此时只能换一种写法:
find ../../ -name *.c | xargs -i grep -rni keyWords {} -color
此方法虽然也能查找到几个调用,但是却无法显示相应文件。
无法让人满意
二、linux shell 显示风格
PS1="\[\e37;40\] [\[\e[32;40m\] \u \[\e[37;40m\] @\h\[\e[36;40m\]\W\[\e[0m\]]\\$"
最后的\\$ 可以替换为 #,如果是\\#则会发生转意,打印操作行数
I、字符串替换小工具
[zhoujinhua@ prj] vi replace.sh
#显然 $# 表示参数的个数
if [ "$#" -lt "2" ];then
echo 'error, please input src_str dst_str'
exit
fi
# $1 表示第一个参数e
echo $1
echo $2
#先把包含关键字 $1 的文件打印出来
echo `grep -rn $1 ./ -rl`
# 所所有包含关键字 $1 的文件,里面的 $1 都替换成 $2
sed -i -e 's/$1/$2/g' `grep -rn $1 ./ -rl`
此脚本调用方法:
[zhoujinhua@ prj] ./replace.sh str_source str_dest
II、 shell 下程序级联调用
#!/bin/sh
python a.py
# 如果 a.py 返回值大于等于128,则执行 b.py
if [ $? -ge 128 ]; then
python b.py
fi
二、patsubst
函数的级联使用方法
obj = $(patsubst %.c,%.o, $(patsubst %.S, %.o, $(patsubst %.s, %.o,$(DIR))))
解释:
1、$(DIR) 为一个目录,是一个路径变量,其展开后,可以是 *.c *.h *.s *.S ...
2、先执行第三个 patsubst:
$(patsubst %.s, %.o,$(DIR)) 将其中所有 *.s 变成 *.o
3、再执行第二个 patsubst:
$(patsubst %.S, %.o, $(patsubst %.s, %.o,$(DIR))) 将所有 *.S 变成 *.o
4、最后执行第一个patsubst,将所有 *.c 变成 *.o
三、echo -e
的作用,man帮助的解释是,允许后面的输出进行转义
假设你是 echo -e "i will use \n $HOME" 输出的将是
i will use
/root(当前用户的主目录)
如果是 echo "i will use \n $HOME" 则输出是:
i will use \n $HOME
四、tar
命令的使用
1、正常打包
tar -cvf ./abc.tar ./
2、排除某个目录打包
tar -vcf ./abc.tar ./ --exclude=./dir1 --exclude=./dir2/release
注意最后一个字符:
是 --exclude=./dir1 而不是 --exclude=./dir1/
五、shell
调试手段
1、相关资源
https://blog.csdn.net/u013516966/article/details/50913770
2、行号$LINENO
,类似C语言__LINE__
3、函数名FUNCNAME
,类似C语言__func__
它是一个数组,${FUNCNAME[0]}为正在执行的函数名。而[1]是[0]的使用者,其余可以类推
4、为sh -x a.sh
调试加上行号和函数名
$PS4 第四级提示符变量,实际调试 sh -x 时,每行前打印的就是 $PS4,在 Bash Shell 中,缺省的 $PS4 值为 '+'号。
利用此特性,通过一些内置变量重定义 $PS4 的值,即可达到增加调试的目的。
如: export PS4='+{$LINENO:${FUNCNAME[0]}}'
然后在调试时添加 -x 选项执行脚本,就能得到第一句执行的行号及函数名
5、调试技巧set -v
在每次执行语句时,希望看到执行的具体内容,当然采用 sh -x a.sh 的办法也是可以的
这里介绍 set -v; xxx ; set +v
如:
set -v
echo good
set +v
六、shell 脚本参数
$# 表示参数个数
$1 表示第一个参数,$2 表示第二个参数 ...
$@ 表示参数列表,可以不做改动,继续向下面的函数传递
七、 shell
脚本 shift
命令的作用
1、shift 作用解释
如 shell 脚本的传入参数为 a b c d e
在脚本中执行一句 shift 2
则将会把入参变成: c d e
2、脚本样例
s
八、强制执行 shell 脚本生效
shell 脚本下,a.sh 中有一句:
cd ../../xx
切换到其它目录的语句。但是执行结束后,仍然在本地,没有实际切换过去
因为在 shell 脚本 a.sh 中,这一句执行完成之后,环境变量就释放了
如果需要使退出后仍然生效,可以这样写:
source cd ../../xx
然后再干别的操作。
或者另一种写法: cd ../../xx ; b.sh 也能达到效果,因为在同一行,环境变量还没有释放
九、获取 python 脚本的返回值,用于逻辑判断
1、例1
A、python 脚本的内容,返回一个值128
pi@raspberrypi:~/study/python $ cat a.py
exit(128)
pi@raspberrypi:~/study/python $
B、shell 脚本,判断其是否大于等于128
#!/bin/sh
python ./a.py
tmp=$?
echo ==$tmp==
if [$tmp -ge 128]; then # 这里是错误语法
echo "great or equal to 128"
else
echo "little than 128"
fi
pi@raspberrypi:~/study/python $
C、注意事项,以上代码为错误代码,需要更正
pi@raspberrypi:~/study/python $ cat b.sh
#!/bin/dash
python ./a.py
tmp=$?
echo ==python return value : $tmp==
if [ "$tmp" -ge "128" ]; then
echo "great or equal to 128"
else
echo "little than 128"
fi
pi@raspberrypi:~/study/python $
i、注意1
shell 赋值语句,变量和值之间不能有空格,用等号直接相连
tmp = $? ==>> 这是错误的写法
tmp=$? ==>> 正确写法
ii、判断语句,中括号与变量之间,必须有空格,否则报错
if [$tmp -ge 128]; then ==>> 写法错误,因为 $tmp 与 [ 之间没有空格
if [ $tmp -ge 128 ]; then ==>> 正确写法
iii、大小符号意义
-eq 等于,如:if [ "$a" -eq "$b" ]
-ne 不等于,如:if [ "$a" -ne "$b" ]
-gt 大于,如:if [ "$a" -gt "$b" ]
-ge 大于等于,如:if [ "$a" -ge "$b" ]
-lt 小于,如:if [ "$a" -lt "$b" ]
-le 小于等于,如:if [ "$a" -le "$b" ]
< 小于(需要双括号),如:(("$a" < "$b"))
<= 小于等于(需要双括号),如:(("$a" <= "$b"))
> 大于(需要双括号),如:(("$a" > "$b"))
>= 大于等于(需要双括号),如:(("$a" >= "$b"))
iv、注意,raspBeryyPi推荐系统,运行的脚本是 /bin/dash
#!/bin/dash 而不是 #!/bin/sh
但是后来为什么两者效果没有差别?
D、最终执行效果
pi@raspberrypi:~/study/python $ cat a.py
exit(138)
pi@raspberrypi:~/study/python $ sh b.sh
==python return value : 138==
great or equal to 128
pi@raspberrypi:~/study/python $
pi@raspberrypi:~/study/python $ sh b.sh
==python return value : 118==
little than 128
pi@raspberrypi:~/study/python $
十、采用歪点子隐藏 shell 脚本输出
1、故意grep
查找一个不存在的字符串
cd - 这条语句会打印新路径
cd - | grep error 这样就不会有输出
十一、shell 循环
1、for
循环,算术类循环
A、风格一
#!/bin/bash
for((i=1;i<=10;i++));
do
echo $(expr $i \* 3 + 1);
done
B、风格二
#!/bin/bash
for i in $(seq 1 10)
do
echo $(expr $i \* 3 + 1);
done
C、风格三
#!/bin/bash
for i in {1..10}
do
echo $(expr $i \* 3 + 1);
done
D、风格四
awk 'BEGIN{for(i=1; i<=10; i++) print i}'
2、字符性质循环
A、将当前目录的所有文件采用ls
列出,然后逐一打印出来
#!/bin/bash
for i in `ls`;
do
echo $i is file name\! ;
done
运行效果:
15900@DESKTOP-CGPRCF3 /cygdrive/d/bbbbbbbbb
$ sh test.sh
get_pic.bat is file name!
IMG_20181205_201517.jpg is file name!
IMG_20181207_195704.jpg is file name!
IMG_20181207_195721.jpg is file name!
IMG_20181207_195736.jpg is file name!
IMG_20181207_195749.jpg is file name!
IMG_20181207_195751.jpg is file name!
IMG_20181207_195755.jpg is file name!
IMG_20181208_123555.jpg is file name!
IMG_20181208_123826.jpg is file name!
IMG_20181208_184448.jpg is file name!
IMG_20181208_184458.jpg is file name!
IMG_20181208_184506.jpg is file name!
test.sh is file name!
B、将 shell 脚本参数逐个打印出来
$ cat test.sh
#!/bin/bash
echo para num is $#;
for i in $* ;
do
echo $i is input chart\! ;
done
执行效果:
$ sh test.sh para1 para2 para3
para num is 3
para1 is input chart!
para2 is input chart!
para3 is input chart!
C、数组、列表类循环
#!/bin/bash
for i in f1 f2 f3 ;
do
echo $i is appoint ;
done
运行效果:
15900@DESKTOP-CGPRCF3 /cygdrive/d/bbbbbbbbb
$ sh test.sh
f1 is appoint
f2 is appoint
f3 is appoint
D、字符串中的变量循环
15900@DESKTOP-CGPRCF3 /cygdrive/d/bbbbbbbbb
$ cat test.sh
#!/bin/bash
list="rootfs usr data data2"
for i in $list;
do
echo $i is appoint ;
done
15900@DESKTOP-CGPRCF3 /cygdrive/d/bbbbbbbbb
$ sh test.sh
rootfs is appoint
usr is appoint
data is appoint
data2 is appoint
3、路径查找类循环
A、查找用户wishcell
下所有文件,不进行遍历
#!/bin/bash
for file in /home/wishcell/*;
do
echo $file is file path \! ;
done
[root@localhost server]# sh test.sh
/home/wishcell/app-debug.apk is file path !
/home/wishcell/jquery-3.1.1.js is file path !
/home/wishcell/jquery-3.1.1.min.js is file path !
/home/wishcell/ngrok.exe is file path !
/home/wishcell/poc_new.py is file path !
/home/wishcell/poc.py is file path !
[root@localhost server]#
B、查找当前目录下的所有.sh
文件
[root@localhost server]# cat test.sh
#!/bin/bash
for file in $(ls *.sh)
do
echo $file is file path \! ;
done
[root@localhost server]#
[root@localhost server]# ls
a.c a.c~ a.sh b.sh server.c server.c~ test.sh
[root@localhost server]# sh test.sh
a.sh is file path !
b.sh is file path !
test.sh is file path !
[root@localhost server]#
4、for
循环嵌套
for file in `cat file_list`
do
for line in `cat $file`
do
echo $line;
done
done
十二、坑
1、变量初始化,赋值
a=3 成功
a = 3 失败
2、变量在双引号中能正常替换,在单引号中变成了常量
a=3
echo "$a" 正常显示3
echo '$a' 显示$a,是一个字符串,十分坑人
3、超级巨坑
本以为只有单引号才是坑。
没想到双绰号的坑更大,坑了好长时间才回过味来,多少个夜晚AO过了。
linux命令行输入如下内容,反应完全不同:
[wishcell@localhost config]$ ls
db.cfg grpc.cfg
加了双引号这是普通字符串
[wishcell@localhost config]$ echo "a *"
a *
不加双引号,就变成了 ls 的作用。做梦也没想到。尤其是在脚本程序中:
[wishcell@localhost config]$ echo a *
a db.cfg grpc.cfg
十三、实际项目
1、项目名称:自动匹配IP,然后登录到相应的linux服务器
A、基础知识:字符串比较玩法
i、判断字符串的包含关系
15900@DESKTOP-CGPRCF3 /cygdrive/d/sync_picture
$ if [ "abcd"=~"abc" ];then echo "good"; fi
good
ii、判断字符串是否为空(1)
15900@DESKTOP-CGPRCF3 /cygdrive/d/sync_picture
$ mystr=
15900@DESKTOP-CGPRCF3 /cygdrive/d/sync_picture
$ if [ $mystr -eq ]
> then
> echo "mystring is empty";
> fi
mystring is empty
15900@DESKTOP-CGPRCF3 /cygdrive/d/sync_picture
$ if [ $mystr -eq ]; then echo "mystring is empty"; fi
mystring is empty
iii、判断字符串是否为空(2)
15900@DESKTOP-CGPRCF3 /cygdrive/d/sync_picture
$ if [ -n $mystr ]; then echo "mystring is empty"; fi
mystring is empty
iv、判断字符串为空(3)
15900@DESKTOP-CGPRCF3 /cygdrive/d/sync_picture
$ if [ "$mystr"=="" ]; then echo "mystring is empty"; fi
mystring is empty
15900@DESKTOP-CGPRCF3 /cygdrive/d/sync_picture
$ if [ x"$mystr"==x ]; then echo "mystring is empty"; fi
mystring is empty
B、基础知识:函数的定义与用法
i、函数定义
function Match_and_connect(){
echo "file name : $1"
echo "line content: $2"
echo "file name key : $3"
echo "line content key : $4"
return
}
ii、函数的调用
# $(Match_and_connect "file_name" "line_content" "file_name_key" "line_content");
不需要考虑返回值的调用方法:
Match_and_connect "file_name" "line_content" "file_name_key" "line_content"
2、具体的项目代码
A、项目脚本用法
$ sh auto_login.sh j_masterstart.sh
B、auto_login.sh 源码
# 首先执行传进来的确参数(其实是一个脚本)
sh $1 ; echo $?
sleep 1s
script_name=`echo $1 | tr a-z A-Z`
old_IFS=$IFS
IFS=$'\n'
the_ip=
function Match_and_connect()
{
echo "script name : $1"
echo "line content: $2"
echo "script name key : $3"
echo "line content key: $4"
if [[ "$1" =~ "$3" ]];then
if [[ "$2" =~ "$4" ]];then
the_ip=`echo $2|tr -d "$4 IP:"`
ssh $the_ip -l root
break
fi
fi
}
# grep -i j_ RADME.txt -A 5 : 从 README中搜索关键字 "j_",不区分大小写,然后找到它,连同其后5行,共六行输出
# tr a-z A-Z 将输出中小写字符转换为大写
for line in `grep -i j_ RADME.txt -A 5 | tr a-z A-Z`
do
echo "----- $line -----"
$(Match_and_connect "$script_name" "$line" "MASTER" "MASTER" );
$(Match_and_connect "$script_name" "$line" "SLAVE" "SLAVE" );
$(Match_and_connect "$script_name" "$line" "LINEONE" "LINEONE");
$(Match_and_connect "$script_name" "$line" "LINETWO" "LINETWO");
if [ -n "$the_ip" ];then
break
fi
done
IFS=$old_IFS
C、README.txt中内容的样子
......
I_server
master ip:192.168.0.60
slave ip:192.168.0.61
lineone ip:192.168.0.78
linetwo ip:192.168.0.70
USER
J_server
master ip:192.168.0.100
slave ip:192.168.0.101
lineone ip:192.168.0.88
linetwo ip:192.168.0.80
USER
K_server
master ip:192.168.0.110
slave ip:192.168.0.111
lineone ip:192.168.0.98
linetwo ip:192.168.0.90
USER
......
D、脚本的样子
......
i-masterstart.sh i-slavestart.sh i-lineonestart.sh i-linetwostart.sh
......
4、运行效果
十四、向批量文件中插入内容
1、需求描述
有134个`.c`文件,需要向各个文件中插入一个`aa.h`,将其插入在第一行即可。
2、解决办法及步骤
A、将所有文件集中到一个文件中
find ./ name "*.c" > list
B、对`list`中的文件逐一操作,`134`个文件瞬间完成
cat list | xargs sed -i '1i\#include \"aa.h\"'
十五、快速解决编译错误,进入编译状态
1、需求描述
编译过程中,由于依赖某个头文件。造成整个目录中百余个文件都需要添加一行头文件引用。
但是每次改完之后,都需要重新编译,才会知道下一个文件是什么名字。大量的编译和查找时间浪费,
人肉操作极其耗时、枯燥、乏味
2、自制工具,编译失败时自别哪个文件缺少头文件。主动打开,等待手工操作,这是半自动化。手工添加后保存退出
> touch qmake.sh
> chmode +x qmake.sh
>
> qmake.sh 中的内容如下
> clear; make > log 2>&1
>
> 注释:先把保存的 log 打印出来,然后过滤掉包含有 BOOL 行,因为具体项目中有干扰。最后再把包含有 error 关键字的行及其前一行打印打印出来。正好作为字符串变量,赋值给 str
> str=`cat log | grep -v BOOL | grep error -B 1`
>
> 这里是一个调试打印
> echo "str is :$str"
>
> filter_name_and_line(){
> 这是调试打印,正式代码不能存在,否则就是错误代码
> # echo "para num:$# / all paras:$@ / para1:$1 / para2:$2..."
> ret_str=`echo $2 | tr -s "[:]" "[ ]"`; echo $ret_str
> }
>
> 将字符串全局变量输入后,得到一个 文件名,空格,行数,这是一个调试打印
> echo $(filter_name_and_line $str)
>
> vim 已经可以粗略的对这个字符串进行编辑了,但是实上它带了两个参数,vim误把它当成两个文件。所以这个需要改进
> vim $(filter_name_and_line $str)
>
> 把文件名、空格、等号的字符串,仅仅提取出文件名。只需要一个函数,把这个字符串当成参数传入,然后在函数体内,会把它当成多个参数,仅仅打印第一个参数就能够达到目标
> filter_only_file(){
> echo $1
> }
>
> 注释:把之前的结果,filter_name_only 的参数,即达到目标。这是一个调试打印
> echo $(filter_only_file $(filter_name_and_line $str))
>
> 最终成品的样子
> vim $(filter_only_file $(filter_name_and_line $str))
3、实际使用
每次输入 sh ./qmake.sh 后就会进入到一个 `.c`的打开状态,手动在里面添加一个头引用后。保存退出。
再次执行 ./qmake.sh,周而复始...
十六、快速用vimdiff
对比目录,其中有大量同名文件
1、需求描述
有两个目录,存在大量同名文件,需要逐一对比,但是手工执行 `vimdiff /path1/file1 /path2/file1`效率很低,
不符合一个程序员的工作风格
2、思路
一个目录,经过不同人员修改,现在需要合并而已
首先获得文件列表。`ls /path/`得到全都是不带路径的文件名
ls /path/
file1 file2 file3 file4 file5 ...
然后每次对比一对文件。根据输入的索引不同,对比的文件名不同
3、根据BDD
思想,需要先确定业务的使用方式,最后开发
用法:
> ```perl
> $ ./qvimdiff.sh
> 报错,提示需要带一个索引参数。表示第几个文件
>
> $ ./qvimdiff.sh 1
> 然后进入 vimdiff ,对比两个文件 /path1/file1 和 /path2/file1
>
> $ ./qvimdiff.sh 2
> 然后进入 vimdiff ,对比两个文件 /path1/file2 和 /path2/file2
4、代码实现
if [ $# -lt 1];
then
echo "please input at least 2 parameter"
echo "usage: ./qvimdiff.sh 1"
exit
fi
此函数有两个参数,第一参数为索引,第二个是文件列表
show_nth_para(){
因为函数有两个参数,但是第二个参数是以空格相隔的字符中。所以对于 shell 函数来说,实际上是有N+1个参数的,N是文件列表的个数。所以真正的file1,它的索引为2,以此类推
file_idx=$(($1+1))
I=0
while 后面的中括号,必须和参数之间保留一个空格,否则报错
while [ $I -lt $file_idx ]
do
把后面的参数转移到最前而,shift 执行一次,转一下
shift
变量I自加操作
I=$(($I+1))
done
这就是转了N次之后,所得到的第N个文件名,把它打印出来,供函数调用都提取返回值
echo "/path1/$1 /path2/$1"
}
print_dir_nth_file(){
ls 是一个函数,/path1/ 是一个参数,返回值赋值给 ls_list
ls_list="$(ls /path1/)"
这里的$1是函数调用者传递过来的,表示文件名索引,第几个文件
show_nth_para $1 $ls_list
}
这里的 $1 是linux 命令行传递进来的,表示文件名索引
vimdiff $(print_dir_nth_file $1)
5、对qvimdiff.sh
进行升级
A、需求描述
由于对比的目录太多。一个大目录./root
下有许多小文件夹。以上脚本不够强大,无法支持。
> ####现在希望,在`./root `下执行脚本,能把`root/dir1/source/a.c`,`root/dir1/include/a.h`以及`./root/dir2/source/b.c`,`./root/dir2/include/b.h` 都列举出来,并采用 `vimdiff`打开。
>
> ####如果`./root/dir2/source/a.c`不存在,则将`./root/dir1/source/a.c`删除
B、按照BDD
设计思想,先把产品用法列出,列出实例化用例
> case 1: 仅仅在命令行输入 ./qvimdiff.sh ,后面不带文件索引参数,脚本应该报错并退出
>
> case 2,两个文件都存在,则用 vimdiff 打开两个文件,并处于编辑状态
> $ ./qvimdiff 1
> $ ./qvimdiff 2
> $ ./qvimdiff 3
> $ ./qvimdiff 4
>
> case 3: ./root/dir2/source/a.c 不存在,则要求删除 ./root/dir1/source/a.c,给出提示并退出
C、代码实现
第一部分,参数检查
if [ $# -lt 1 ]; then
echo "error , please input file_idx"
echo "usage: ./qvimdiff.sh 1"
exit
fi
show_nth_para(){
str_idx=$(($1+1))
临时变量只能采用大写字母,不能小写
I=0
while [ $I -lt $str_idx ]
do
shift
I=$(($I+1))
done
file1=$1
这是一个 shell 正则表达式,将变量 file1 中的字符串 vomci 替换为 vomci2
file2=${file1/vomci/vomci2}
判断,如何第二个文件不存在
if [ ! -f $file2 ]; then
rm $file1
echo "$file1 is removed"
exit
fi
echo "$file1 file2"
}
print_nth_file(){
find_str=`find $PWD/dir -name "*.[c|h]"`
show_nth_para $1 $find_str
}
调试打印,看看输出是否符合预期
# print_nth_file $1
正式代码,这个 $1 是命令行传进来的第一个参数
vimdiff $(print_nth_file $1)
D、对qvimdiff.sh
再次升级,只列列出不同文件,跳过相同文件
用到的知识点:
shell 下如何判断文件是否相同
首先看两个文件是否都存在`if [ ! -f file_1 ]; then`
然后用`md5sum`命令对两个文件计算,输出一个32位的字符串,后跟文件名
sum1=`md5sum -t $file1`
sum2=`md5sum -t $file2`
`md5sum`工具需要`root`权限才能使用
`md5sum`工具得到的字符串,前 32 位可以用来做对比,决断文件是否相
将前面`md5sum`
得到的字符串进行截取前`32`位。`id=${sum1:0:32}`
遍历函数所所有入参:
myFunc() {
for i in $@
do
file1=$i
shift
done
}
myFunc aa bb cc dd
也可以写成
myFunc(){
for i in $@ ;
do {
file1=$i
shift
echo $file1 is file name
}
done
}
myFunc "a b c d"
还可以写成
myFunc() {
for i in $@ ; do {
file1=$i
shift
echo $file1 is file name
}
done
}
myFunc "a b c d"
myFunc() {
for i in $@ ; do
{
file1=$i
shift
echo $file1 is file name
} done
}
myFunc "a b c d"
利用函数返回值对比,而不采用中间变量
isFileSame $file1 $file2
if [ "$?" == "0" ]; then
echo "file not same"
fi
完整的升级代码,实现对目录./
下所有不同文件用vimdiff
打开,相同文件跳过
十六、shell
字符串正则表达式
1、采用grep
来给搜索的内容上色
十七、shell
字符串截取
假设有变量 var=http://www.aaa.com/123.htm
1. ‘#’ 号截取,删除左边字符,保留右边字符。
echo ${var#*//}
其中 var 是变量名,\# 号是运算符,*// 表示从左边开始删除第一个 // 号及左边的所有字符
即删除 http://
结果是 :www.aaa.com/123.htm
2. “##” 号截取,删除左边字符,保留右边字符。
`echo ${var##*/}`
##*/ 表示从左边开始删除最后(最右边)一个 / 号及左边的所有字符
即删除 http://www.aaa.com/
结果是 123.htm
3. “%”号截取,删除右边字符,保留左边字符
echo ${var%/*}
%/* 表示从右边开始,删除第一个 / 号及右边的字符
结果是:http://www.aaa.com
4. “%%” 号截取,删除右边字符,保留左边字符
echo ${var%%/*}
%%/* 表示从右边开始,删除最后(最左边)一个 / 号及右边的字符
结果是:http:
5. 从左边第几个字符开始,及字符的个数
echo ${var:0:5}
其中的 0 表示左边第一个字符开始,5 表示字符的总个数。
结果是:http:
6. 从左边第几个字符开始,一直到结束。
echo ${var:7}
其中的 7 表示左边第8个字符开始,一直到结束。
结果是 :www.aaa.com/123.htm
7. 从右边第几个字符开始,及字符的个数
echo ${var:0-7:3}
其中的 0-7(其实是负7)表示右边算起第七个字符开始,3 表示字符的个数。
结果是:123
8. 从右边第几个字符开始,一直到结束。
echo ${var:0-7}
表示从右边第七个字符开始,一直到结束。
结果是:123.htm
9、获取全路径中,文件名的方法:basename
# ls study/c/aa.c
study/c/aa.c
# basename study/c/aa.c
aa.c
十八、shell
调试报错
1、括号不配对情况,关键字不配对情况
syntax error:unexpected end of file
十九、项目尝试自动删除多余的头文件。保证编译成功
> 但是此方案有一个缺陷,会造成某些重要函数声明丢失,进而引起严重故障,而且难查
>
> 优点:对于一个成熟项目,重复文件越少,检查越快,因为每个都是必不可少的引用,缺少则编译失败。而垃圾引用越多,检查就越慢,因为垃圾引用去除后,还要完整编译。不能增量编译
# 针对当前目录下所有源文件
list=`find ./ -name "*.c"`
for file_name in ${list}; do
# 逐个文件提取 #include 的头文件行数。并将行号倒序,从大到小排列,
# 方便从大到小行删除试错
lines=`cat -n ${file_name} | grep '#include' | awk '{print $1}' | tac`
tmp_content=''
# 对单个 #include 进行尝试
for line in ${lines}; do
# 首先把这一行的 #include 内容原样保存,以防编译失败
tmp_content=`sed -n "${line}p" ${file_name}`
# 然后删除当前行的 #include 语句
sed -i "${line}d" ${file_name}
# 开始版本编译
make rm
if [ $? -eq 0 ]; then
# 如果编译成功,则提示通过
echo "pass"
else
# 如果编译失败,则将之前保存的当前 #include 内容插回到文件,将其恢复
sed -i "${line} i${tmp_centent}" ${file_name}
fi
done
done
二十、采用sed
实现的删除代码注释的功能(不够强大)
0、shell 基础判断
if [ -n ${str} ] 如果字符串长度大于 0
if [ -z ${str} ] 如果字符串长度等于 0
if [ str1 != str2 ]
if [ str1 == str2 ]
1、第一种实现方案
full_name=$1
dst_file=$2
if [ -z ${full_name} ]; then
echo "no source file input"
exit
fi
# 将完整路径以 / 分割,提取文件名,/home/path/abc.sh,则得到 abc.sh
short_name=`echo ${full_name} | tr -s '/' '[ ]' | awk '{print $NF}'`
# 把短文件名以'.' 分割,取第二段,即文件类型后缀,得到 sh
#`echo look.abc.sh.as.k | cut -f 1 -d '.'` ==>> look
#`echo look.abc.sh.as.k | cut -f 2 -d '.'` ==>> abc
#`echo look.abc.sh.as.k | cut -f 3 -d '.'` ==>> sh
post_fix=`echo ${short_name} | cut -f 2 -d '.' `
# 如果指定的接收注释后的文件名为空
if [ "${dst_file}" == "" ]; then
#得到以'.'分割的完整路径的倒数第二段。若是 ./path/a.c 则得到 /path/a
prefix=`echo ${full_name} | tr -s '[.]' '[ ]' | awk '{print $(NF - 1)}' `
# 判断是否是以 / 开头
first_char=`echo ${pre_fix}|grep ^\/`
if [ -n ${first_char} ]; then
#如果是以 / 开头,需要把 "."添加回去
prefix="."${prefix}
fi
#得到一个临时文件
target_file=${prefix}.temp
touch ${target_file}
fi
case "${post_fix}" in
sh )
sed "s/[[:space]]#.*//g" ${full_name} | sed '/^#/d' | \
sed '/^[[:space:]]*$/g' | sed '/^$/d' > ${target_file}
;;
h | c | java )
sed 's/\/\*.*\*\///g' ${full_name} | sed '/\/\*/,/.*\*\//d' | \
sed 's/\/\/.*//g' | \
sed '/s^[[:space:]]*$/d' | sed '/^$/d' > ${target_file}
;;
* )
echo 'unknown file type'
rm ${target_file}
esac
2、第二种实现方案
function delete_comment_file()
{
file_name=$1
#删除可能以空格开头的, //风格的注释
sed -i '/^[ \t]*\/\//d' ${file_name}
# 试图删除半路 // 风格的代码,但是保留例个,如代码中存在ftp://www.baidu.com 这种代码时
# 但是实际效果没有保留成功
sed -i 's/\/\/[^"]*//' ${file_name}
# 删除C语言最基本的单行注释 /* assd */
sed -i 's/\/\*.*\*\///' ${file_name}
# 删除C语言块注释
# /* adi
# adf */
sed -i 's/^[ \t]*\/\*,/.*\*\//d' ${file_name}
}
function delete_comment()
{
for file in `ls`; do
case ${file} in
*.c )
delete_comment_file ${file} ;;
*.cpp )
delete_comment_file ${file} ;;
*.h )
delete_comment_file ${file} ;;
* )
if [ -d ${file} ]; then
cd ${file}; delete_comment; cd ..
fi ;;
esac
done
}
my_dir=$1
if [ ! -e ${my_dir} ]; then
echo "empty dir"
else
cd ${my_dir}
delete_comment
fi
二十、数组
1、数组定义
[root@localhost shell]$foo=(a b c)
[root@localhost shell]$echo ${foo[@]}
a b c
[root@localhost shell]$echo ${foo[0]}
a
[root@localhost shell]$echo ${foo[1]}
b
[root@localhost shell]$echo ${foo[2]}
c
2、数组追加元素
[root@localhost shell]$foo+=(d e f)
[root@localhost shell]$echo ${foo[5]}
f
[root@localhost shell]$echo ${foo[@]}
a b c d e f
[root@localhost shell]$echo ${foo[*]}
a b c d e f
[root@localhost shell]$echo ${#foo[*]}
6
[root@localhost shell]$echo ${#foo}
1
[root@localhost shell]$echo ${foo}
a
[root@localhost shell]$echo ${#foo[*]}
6
[root@localhost shell]$echo ${#foo[1]}
1
[root@localhost shell]$echo ${#foo[6]}
0
[root@localhost shell]$echo ${#foo[2]}
1
[root@localhost shell]$echo ${#foo[@]}
6
3、数组用法,引用元素和全部数组
[root@localhost shell]$echo ${foo[5]}
f
[root@localhost shell]$echo ${foo[@]}
a b c d e f
[root@localhost shell]$echo ${foo[*]}
a b c d e f
4、数组长度
[root@localhost shell]$echo ${#foo[*]}
6
[root@localhost shell]$echo ${#foo[1]}
1
[root@localhost shell]$echo ${#foo[6]}
0
[root@localhost shell]$echo ${#foo[2]}
1
[root@localhost shell]$echo ${#foo[@]}
6
5、数组所有元素,两种表示方法${foo[@]}和${foo[*]}
的区别
[root@localhost study]$echo "${foo[@]}"
a b c d e f
[root@localhost study]$for i in "${foo[@]}"; do echo $i;done
a
b
c
d
e
f
[root@localhost study]$ for i in "${foo[*]}"; do echo $i; done
a b c d e f
[root@localhost study]$
6、数组的删除
[root@localhost study]$echo ${foo[@]}
a b c d e f
[root@localhost study]$unset foo
[root@localhost study]$echo ${foo[@]}
[root@localhost study]$
二十一、ls
的乐趣
1、同时查看多个目录
[root@localhost study]$ls c/ tmp/
c/:
a.out ftp gtest-1.7.0.zip option ss.c tt.c
find_ip ftp2 hello.c option.c test_readdir webserver
find_ip.c ftp3 ip_result pipe.c test_readdir.c
find_ip.sh gtest-1.7.0 massif.out.7238 ss.b tt
tmp/:
wine-3.0.tar.xz
[root@localhost study]$
2、长格式查看
[root@localhost study]$ls c -l
total 1324
-rwxrwxrwx. 1 wishcell wishcell 8824 Jun 18 2018 a.out
-rwxrwxr-x. 1 wishcell wishcell 8560 Nov 24 14:01 find_ip
-rw-rw-r--. 1 wishcell wishcell 150 Nov 24 14:01 find_ip.c
-rw-rw-r--. 1 wishcell wishcell 6194 Nov 24 14:02 find_ip.sh
drwxrwxrwx. 4 wishcell wishcell 34 Jun 19 2018 ftp
drwxrwxrwx. 3 wishcell wishcell 17 Jun 19 2018 ftp2
[root@localhost study]$
关键字说明:
[root@localhost study]$ls c -l
total 1324
-rwxrwxrwx. 1 wishcell wishcell 8824 Jun 18 2018 a.out
-rwxrwxr-x. 1 wishcell wishcell 8560 Nov 24 14:01 find_ip
-rw-rw-r--. 1 wishcell wishcell 150 Nov 24 14:01 find_ip.c
-rw-rw-r--. 1 wishcell wishcell 6194 Nov 24 14:02 find_ip.sh
drwxrwxrwx. 4 wishcell wishcell 34 Jun 19 2018 ftp
drwxrwxrwx. 3 wishcell wishcell 17 Jun 19 2018 ftp2
第一列第一个字符,表示类型。
`-`表示普通文件
`d`表示目录
`l`表示链接
二十二、cp/mv
查漏补缺
1、-r
参数,表示递归
2、-u
参数,表示只复制目标文件夹中不存在的文件
3、-i
参数,提醒用户确认,是否覆盖f
二十三、alias
命令
1、查看系统中的别名
[wishcell@localhost tmp]$ alias
alias docker-machine='__docker_machine_wrapper'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias vi='vim'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
2、自己定义一个别名
[wishcell@localhost tmp]$ alias foo='cd /usr;ls;cd -'
[wishcell@localhost tmp]$ foo
bin games lib libexec sbin src
etc include lib64 local share tmp
/home/wishcell/study/tmp
[wishcell@localhost tmp]$
[wishcell@localhost tmp]$ type foo
foo is aliased to `cd /usr;ls;cd -`
[wishcell@localhost tmp]$
3、删除别名
[wishcell@localhost tmp]$ unalias foo
[wishcell@localhost tmp]$ type foo
-bash: type: foo: not found
[wishcell@localhost tmp]$
二十四、head/tail
命令
1、默认指定文件的前/后
10行
首先创建一个101行的文本文件:
[wishcell@localhost dir2]$ echo -e {1..100}"\n" >b.txt
确认文件行数:
[wishcell@localhost dir2]$ cat b.txt | wc -l
101
[wishcell@localhost dir2]$ head b.txt
1
2
3
4
5
6
7
8
9
10
[wishcell@localhost dir2]$ tail b.txt
92
93
94
95
96
97
98
99
100
2、-n
参数指定行数
[wishcell@localhost dir2]$ head -n 5 b.txt
1
2
3
4
5
[wishcell@localhost dir2]$ tail -n 5 b.txt
97
98
99
100
[wishcell@localhost dir2]$
二十五、花括号{}
扩展
1、基础示例
[wishcell@localhost dir2]$ echo front_{A,B,C}_back
front_A_back front_B_back front_C_back
[wishcell@localhost dir2]$
2、数字展开
[wishcell@localhost dir2]$ echo number_{1..5}
number_1 number_2 number_3 number_4 number_5
[wishcell@localhost dir2]$
3、字母展开
[wishcell@localhost dir2]$ echo {Z..A}
Z Y X W V U T S R Q P O N M L K J I H G F E D C B A
[wishcell@localhost dir2]$
4、复合展开
[wishcell@localhost dir2]$ echo a{A{1,2},B{3,4}}b
aA1b aA2b aB3b aB4b
[wishcell@localhost dir2]$
二十六、linux 命令行,光标控制
1、ctrl+A
光标移到行首
2、ctrl+E
光标移到行尾
3、ctrl+L
清除屏幕,保留当前行
二十七、程序由后台转到前台执行
1、示例
[wishcell@localhost dir2]$ vim &
[1] 29650
[wishcell@localhost dir2]$ fg vim
vim
或者执行 bg %1 也能达到同样效果。但是对后面数字没有把握
二十八、uniq
命令
1、显示重复的内容
[wishcell@localhost dir2]$ cat b.txt
line 1
line 2
line 2
line 3
line 3
line 4
line 5
[wishcell@localhost dir2]$ uniq -d b.txt
line 2
line 3
[wishcell@localhost dir2]$
2、显示不重复的内容,-c
参数可以看到行重复的次数
[wishcell@localhost dir2]$ uniq b.txt
line 1
line 2
line 3
line 4
line 5
[wishcell@localhost dir2]$
[wishcell@localhost dir2]$ uniq b.txt -c
1 line 1
2 line 2
2 line 3
1 line 4
1 line 5
[wishcell@localhost dir2]$
二十九、shell printf
与C
语言十分相似
[wishcell@localhost dir2]$ printf "%d,%f,%o,%s,%x,%X\n" 380 380 380 380 380 380
380,380.000000,574,380,17c,17C
[wishcell@localhost dir2]$
带上类似C语言的控制字符.
[wishcell@localhost dir2]$ printf "%05d,%05.5f,%o,%s,%#x,%#X\n" 380 380 380 380 380 380
00380,380.00000,574,380,0x17c,0X17C
三十、 shell here 文档
[wishcell@localhost shell]$ cat ad.sh
report_uptime(){
cat <<- _EOF_
<H2> System Uptime</H2>
<PRE>$(df -h; uptime)</PRE>
_EOF_
}
report_uptime
[wishcell@localhost shell]$
[wishcell@localhost shell]$ sh ad.sh
<H2> System Uptime</H2>
<PRE>Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 41G 27G 15G 64% /
devtmpfs 905M 0 905M 0% /dev
tmpfs 920M 0 920M 0% /dev/shm
tmpfs 920M 8.9M 911M 1% /run
tmpfs 920M 0 920M 0% /sys/fs/cgroup
/dev/sda1 1014M 179M 836M 18% /boot
/dev/mapper/centos-home 20G 2.2G 18G 11% /home
tmpfs 184M 32K 184M 1% /run/user/1000
23:47:55 up 13:29, 5 users, load average: 0.08, 0.03, 0.05</PRE>
三十一、常用条件表达式
1、文件比较
file1 -ef file2 file1 和 file2拥有相同的信息节点和编号(这两个文件通过硬链接指向同一个文件)
file1 -nt file2 file1 比 file2 新
file1 -ot file2 file1 比 file2 旧
-b file file 存在而且是一个块(设备)文件
-c file file 存在而且是一个字符(设备)文件
-d file file 存在而且是一个目录
-e file file 存在
-f file file 存在而且是普通文件
...
-L file file 存在而且是一个符号链接
...
-r file file 存在而且可读
-s file file 存在并且长度大于0
...
-w file file 存在且可定
-x file file 存在且可执行
2、字符串比较
[ string ] string 不为空
-n string 长度大于0
-z strig 长度等于0
string1=string2
string1==string2
string1!=string2
string1>string2 排序时,string1 在 string2 之后 。实际使用时,">","<"都需要斜杠转意
string1<string2
3、整数比较
a -eq b 相等
a -ne b 不等
a -le b 小于等于
a -lt b 小于
a -ge b 大于等于
a -gt b 大于
三十二、case
语句
case $(var) in
a) echo '关键字为 a 则成立' ;;
[[:alpha]] ) echo '关键字为单个字母则吻合' ;;
???) echo '关键字为三个字符则吻合' ;;
*.txt) echo '以 .txt 结尾则吻合' ;;
*) echo '其余条件吻合' ;;
esac
三十三、强大的搜索应用
1、shell
搜索函数在哪个文件中定义
# 比如要搜索 sleep 函数在当前目录哪个文件中定义:
[wishcell@localhost shell]$ cat aa.sh
funName=sleep
grep -rn "\w\+[ ]\+${funName}([^()]*)[ ]*{\?$" ./
# 带调度信息的调用:
[wishcell@localhost shell]$ sh -x aa.sh
+ funName=sleep
+ grep -rn '\w\+[ ]\+sleep([^()]*)[ ]*{\?$' ./
./monitor.cc:305:void sleep()
[wishcell@localhost shell]$
#不带调度信息的调用:
[wishcell@localhost shell]$ sh aa.sh
./monitor.cc:305:void sleep()
[wishcell@localhost shell]$
2、对文件进行优化,仅搜索 C
文件,加快搜索速度
cat file.txt | xargs -i grep -rl {} ../bami/ -type f | grep \.c$
说明: -rl 只显示文件名
-rni既有文件名,又有函数名
3、利用正则表达式,由 C 直接生成头文件(此例完美工作)
[wishcell@localhost shell]$ cat get_head.sh
#!/bin/bash
source_file=$1
if [[ -f ${source_file} ]]; then
grep "\w\+[ ]\+\w\+([^()]*)[ ]*{\?$" ${source_file} | \
grep -v "main" | sed -e 's/{\?$/;/' > ${source_file%.*}.h
fi
# 注释:
# \w\+ 表示多个字符,至少一个,这是函数的返回值类型
# [ ]\+ 表示多个空格,至少一个
# ([^()*]) 这个没有理解
# ==>>>>> [^()]*表示不包含左右小括号(、)的任意字符
# ==>>>>> 最外层的()是函数体的格式,函数名后面的小括号。
# ==>>>>> 就是函数定义的括号内,不能再有括号, 非常正确
# [ ]* 表示任意多个空格,可以是0个
# {\?$ 表示以0个或者一个 '{' 结束,
# 就是说函数定义的行尾是否有半个花括号
# % 符号把匹配成功的东西丢弃掉,这里显然是把文件的后缀丢掉,
# 然后重新添加后缀 .h
4、利用正则表达式,对 lcov 产生的网页操作
进行函数提取,可以感知函数是否被覆盖
[wishcell@localhost c]$ grep -o "[0-9]\+[ ]\+:[ ]\+\w\+[ ]\+\w\+([^()]*)[ ]*{\?" a.c.gcov.html
1 : int add(int a, int b)
1 : int sub(int a, int b)
1 : int main()
[wishcell@localhost c]$
# 故意把 C 函数的花括号放在函数定义的地方,重新实验,效果仍然满意
[wishcell@localhost c]$ grep -o "[0-9]\+[ ]\+:[ ]\+\w\+[ ]\+\w\+([^()]*)[ ]*{\?" a.c.gcov.html
1 : int add(int a, int b){
1 : int sub(int a, int b){
1 : int main() {
[wishcell@localhost c]$
综合应用
1、一个编译脚本,有各种场景
#!/bin/bash
currDir=`pwd`
# 利用项目名称,进行字符串分割,保留项目所在路径
prjHome=${currDir%%prjName*}
buildFlag=$1
if [ -z $buildFlag ]; then
buildFlag="help"
fi
# $1 is color, and $2 is content string
function colorPrint(){
stringContent=$2
if [ "$1" == "red" ]; then
echo -e "\033[41m $stringContent \033[\m"
elif [ "$1" == "green" ]; then
echo -e "\033[42m $stringContent \033[\m"
elif [ "$1" == "yellow" ]; then
echo -e "\033[43m $stringContent \033[\m"
elif [ "$1" == "gray" ]; then
echo -e "\033[47m $stringContent \033[\m"
fi
}
function quitBecauseFail() {
if [ "$1" == "0" ]; then
colorPrint green "$2 build succeed!"
else
colorPrint red "$2 build failed!"
fi
}
if [ "${buildFlag}" == "help" ]; then
set -v
echo "usage: sh buildPrjUT.sh"
echo " sh buildPrjUT.sh gdb"
echo " sh buildPrjUT.sh run"
echo " sh -x buildPrjUT.sh run"
echo " export PS4='+\${LINENO:\${FUNCNAME[0]}}-->> '"
echo " date>mytime; sh buildPrjUT.sh flag 2>&1 | tee myLog; date>>mytime"
set +v
elif [ "${buildFlag}" == "all" ] || [ "${buildFlag}" == "All" ] ; then
set -v
buildFlag="all"
echo "para is ${buildFlag}"
set +v
elif [ "${buildFlag}" == "gdb" ] || [ "${buildFlag}" == "GDB" ] ; then
set -v
buildFlag="gdb"
echo "para is ${buildFlag}"
set +v
else [ "${buildFlag}" == "gdb" ] || [ "${buildFlag}" == "GDB" ] ; then
set -v
buildFlag="gdb"
echo "para is ${buildFlag}"
set +v
fi
cpuNum=$((`grep -c "processor" /proc/cpuinfo` - 1))
echo "cpuNum is only : ${cpuNum}"
buildPath=${prjHome}/buildDir
destFile=${prjHome}/build/tt
toBeChange=${prjHome}/source/src3/aa.c
excludeDir=(${prjHome}/source/src1 \
${prjHome}/source/src2)
if [ "${buildFlag}" == "run" ]; then
if [ -e "${destFile}" ]; then
${destFile} # 如果目标文件已经生成,直接运行
else
colorPrint 'red' "${destFile} not exist" # 否则报错
fi
elif [ "${buildFlag}" == "gdb" ]; then
if [ -e "${destFile}" ]; then
gdb ${destFile} # 如果目标文件已经生成,直接运行
else
colorPrint 'red' "${destFile} not exist" # 否则报错
fi
fi
echo currDir=${currDir}
echo buildPath=${buildPath}
echo toBeChange=${toBeChange}
echo "exclude dir Array:"
for i in "${excludeDir[@]}"; do colorPrint yellow " ${i}";done
set -v
# 将此文件 30~40 行删除
cd $(dirname ${toBeChange}) && git checkout $(basename ${toBeChange}) && sed -i '30,40d' $(basename ${toBeChange})
# 在此文件某关键字处,其后追加几行内容,先找到关键字所在行
lineNum=`sed -n '/keyWord/= ' ${toBeChange}` \
echo ${lineNum}
sed -i "${lineNum}aThis\ is \ New\ content\ string1" ${toBeChange}
sed -i "${lineNum}aThis\ is \ New\ content\ string2" ${toBeChange}
# 删除多余文件,排除干扰
for i in "${excludeDir[@]}"; do rm ${i} -rf; done
set +v
if [ "${buildFlag}" == "all" ]; then
make all
fi
if [ "${buildFlag}" != "all" ]; then
echo "now is building module: ${buildFlag}"
cd ${buildPath}
if [ "${buildFlag}" == "module_1" ]; then
make module_1
quitBecauseFail $? ${buildFlag}
elif [ "${buildFlag}" == "module_2" ]; then
make module_2
quitBecauseFail $? ${buildFlag}
fi
fi
rm ${destFile} -rf
cd ${buildPath} && make all
quitBecauseFail $? ${buildFlag}
if [ -e ${destFile} ] ; then
# 恢复之前临时修改的文件
cd $(dirname ${toBeChange}) && git checkout $(basename ${toBeChange})
for i in "${excludeDir[@]}"; do cd $(dirname ${i}) && git checkout $(basename ${i})"; done
echo "${destFile} build succeed!"
${destFile}
else
colorPrint gray "${targetFile} build failed"
colorPrint red "${targetFile} build failed"
fi
shell数组倒序打印
一、方法一:使用字符串切片
#! /bin/bash
read -p "请输入倒序内容:" str
len=${#str} # 获取字符串长度
for((i=$len;i>=0;i--))
do
echo -e "${str:$i:1}\c" # 其中 -e 是开启转义 \c 取消echo的换行
done
echo ""
二、第二种:利用shell中的数组
#! /bin/bash
read -p "请输入倒序内容:" s
str=`echo $s | sed 's/./& /g'` # 这里将字符串转化为数组格式 h e l l e v e r y o n e
array=($str) # 将字符串转化为数组(h e l l e v e r y o n e)
len=${#array[@]} # 获取数组长度 还有一种方式 len=${#array[*]}
for ((i=$len - 1;i>=0;i--))
do
echo -e "${array[$i]}\c" # 其中 -e 是开启转义 \c 取消echo的换行
done
echo ""
特定场景:将关键字前面的内容复制到其后面
1、需求说明,对于跳转代码的情形
A、无返回值的情况
程序中,存在如下代码:
{
...
return;
//----可能存在若干空行
ERR:
return;
}
B、返回变量
程序中,存在如下代码:
{
...
return ret;
//----可能存在若干空行
ERR:
a=3;
return ret;
}
C、无返常数
程序中,存在如下代码:
{
...
return OK;
//----可能存在若干空行
ERR:
printf("error...\n")
return OK;
}
2、现在需要只要遇到 ERR:
就立即返回错误
`但是不能一概而论,直接写死返回某种形式,因为这样可能造成返回值错误,从而编译失败
理想做法,是根据规律,将 "ERR:"上面的一行返回值语句复制到它下面`
3、解决思路
`1、将 ERR: 之前,可能的空行删除,确保其上紧接的是一条 return 语句
2、利用 sed 工具,获取所有 ERR: 所在行的行号数组
3、将 shell数组倒序处理
3.1 利用 sed -n 3p file 语法,将上一行字符串获取到
3.2 利用 sed -i "3istr" 的方法,将 str 追加到指定行后`
4、具体实现代码
1、删除所有可能的空行(更多的删除方法,参看sed.html)
sed -i "/^[[:space:]]*$/d" ${fileName}
2、利用 sed 工具,获取所有 ERR: 所在行的行号数组
lineNum=`sed -n /ERR:/=` ${fileName}
lineNumArray=(${lineNum}) # 其实即使不这么操作,${lineNum} 自身还是数组
len=${#lineNumArray[@]} #数组长度还有一种表示方法:${#lineNumArray[*]}
3、利用循环语句,对各个行分别处理
for ((i=$len - 1; i>=0; i--))
do
# 获取 `ERR:` 所在行前一行的内容
pre_line_content=`sed -n $((${lineNumArray[$i]}-1))`p ${fileName}
echo "pre_line_content=${pre_line_content}
# 将 ERR: 前一行的内容,追加到其后
sed -i "${lineNumArray[$i]}a${pre_line_content}" ${fileName}
done
大段代码注入到文件中
1、基础用法
cat <<- MyEndFlag > index.html
<html>
<body> this is a tst </body>
</html>
MyEndFlag
其中,MyEndFlag是自己定义的一个开始结束语标志,当然一般规范的写法,都是采取 END 或者EOF.
[Administrator.WINDOWS-LGJ801D] ➤ sh inject.sh
[Administrator.WINDOWS-LGJ801D] ➤ ls
index.html inject.sh
[Administrator.WINDOWS-LGJ801D] ➤ cat index.html
<html>
<body> this is a tst </body>
</html>
2、追加内容到指定文件
只要将重定向符号改为追加即可:
cat <<- MyEndFlag >> index.html
3、空行也直接原样追加,超级省心
[Administrator.WINDOWS-LGJ801D] ➤ cat index.html
<html>
<body> this is a tst </body>
</html>
this is the end
4、此功能对项目工程的应用价值
可以将某段代码直接写入功能代码,毕竟这样写出来的测试用例,编译成本最低。
否则需要解决大量的头文件引用问题,数据结构重定义,等等十分繁琐的事情。
用完之后,清理也十分简单,因为在注入时,脏代码前面都加上了标志。
function hasInjected(){
fileName=$1
injectFlag=`sed -n "/DIRTY_CODE_BEGIN/=" ${fileName}`
if [[ "${injectFlag}" =="" ]]; then
echo "${fileName} has not inject testcase"
return 0
else
echo "${fileName} has inject testcase"
return 1
fi
}
function injectTesting(){
fileName=$1
# judge if dirty code has injected
hasInjected $fileName
if [[ "$?" =="1"]]; then
return # if code has injected, return directively
fi
cat <<- MyEndFlag >> ${fileName}
#define DIRTY_CODE_BEGIN
void dirTest()
{
int para = 3;
funcAtest(para);
}
MyEndFlag
}
#using it like this
injectTesting ${projectHome}/module/funcA.c
5、shell整块输出到屏幕,而不是输出到文件
cat <<- MyEndFlag
#define DIRTY_CODE_BEGIN
void dirTest()
{
int para = 3;
funcAtest(para);
}
MyEndFlag
}
6、既输出到文件,又打印到屏幕
cat <<- MyEndFlag | tee ${fileName}
#define DIRTY_CODE_BEGIN
void dirTest()
{
int para = 3;
funcAtest(para);
}
MyEndFlag
}
shell脚本延时
秒级延时 : sleep 1
延时 10ms: sleep 10
延时 10ms: usleep 1000
一键恢复 git 改动
git status | grep 删除 | awk '{print $3}' | egrep -o ".*/testcase" | \
grep -vw key_1/module/testcase | grep -vw key_2/module/testcase | \
xargs -i git checkout {}
注意事项:
uniq -d 这个选项不能加,加了会达不到目标,就多执行几次 git checkout 吧
grep -vw key1 是因为怕有其它项目,名称为 xxkey1/module/testcase 也被过滤掉
egrep -o ".*/testcase/" 效果不同于 egrep -o .*/testcase/
后一种效果只能匹配到两个字符,前一种是正则表达式匹配所有,这个坑了我一段时间
shell 数组使用,坑
lineNum=`sed -n "/${keyWord}/="` ${fileName}
这里, ${keyword} 外面只能用双引号,一旦用了单引号,结果就会错
结果 clineNum 可能不止一条记录,如果是多条,就需要用数组来存储
lineNumArray=(${lineNum})
如果没有找到关键字,则记录为空,所以需要判断
if [ ${#lineNumArray[@]} == 0]; then
echo key ${keyword} not found from ${fileName}
else
# 将关键字开始,到文件尾的内容全部删除
sed -i "${lineNumArray[0]},$"d ${fileName}
fi