Shell脚本的简单排错法及调试程序bashdb
Jboss 的研究稍有卡壳,那就来点基础教程好了。
与众多脚本语言一样,Shell 脚本在执行时出错是很常见的,最简单的原因无外乎脚本在编写的过程中出现了语法错误或者不小心输错了命令等。找出脚本中的错误是很重要的能力。比如,我经常不小心会把 echo 命令写成了 ehco,那么执行就会出现下面这种情况:
[root@localhost ~]# sh test
test: line 2: ehco: command not found
从报错信息很容易判断出错的原因是“命令不存在”。重新编辑这个文件修改成 echo 就可以解决。如果只是语法或命令上的错误还是比较容易辨别的,但往往一些逻辑或算法错误就不容易发现,因为语法正确且本身不会造成程序运行错误。比如说下面的脚本,本来想连续 10 次做某些操作的,结果却迟迟没输出。仔细观察一下就知道是陷入了死循环。
[root@localhost ~]# cat test
#!/bin/bash
for ((i=10;i>0;i=i+1))
do
#run command there
done
如果在上面的循环中加入 echo 语句,就容易发现问题了。而如果是单次循环过快,根本来不及看就进入了下一次循环,那这时就可以加入 sleep 命令降低单次循环的速度,比如使用 sleep 2,单次循环就将延时 2s,给我们带来足够的观察时间:
[root@localhost ~]# cat test
#!/bin/bash
for ((i=10;i>0;i=i+1))
do
#run command there
echo "i=$i";
done
为了更清晰的看到脚本运行的过程,我们还可以借助-x 参数来观察脚本的运行情况。比如上面的脚本,我们使用-x 参数执行就可以发现,变量 i 的值一直在增加,且一直满足 x>0 的条件,所以这是一个死循环。所以,我们只要将 i=i+1 修改成 i=i-1 即可。
[root@localhost ~]# sh -x test
+ (( i=10 ))
+ (( i>0 ))
+ echo i=10
i=10
+ sleep 2
+ (( i=i+1 ))
+ (( i>0 ))
+ echo i=11
i=11
+ sleep 2
+ (( i=i+1 ))
+ (( i>0 ))
+ echo i=12
i=12
+ sleep 2
[Ctrl +c]终止脚本
Shell 本身并没有提供更好的排错工具,为了更加精细地调试 Shell 脚本,我们可以借助第三方工具 bashdb。这是一个类似于 GDB 的脚本调试软件,小巧而强大,具有这只断点、单步执行、观察变量等功能。下载时请根据所使用的 bash 版本选择相应的 bashdb,否则会提示因为版本不符合而无法安装。
如下查看 bash 版本:
[root@localhost ~]# bash --version
GNU bash, version 3.1.25(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
如下安装:
#第一步:在终端使用wget下载3.1版本
wget http://ftp.jaist.ac.jp/pub/sourceforge/b/ba/bashdb/bashdb/3.1-0.09/bashdb-3.1-0.09.tar.gz
#第二步:解压并进入目录
tar -zxvf bashdb-3.1-0.09.tar.gz
cd bashdb-3.1-0.09
#第三步:配置及编译安装
./configure
make && make install
安装完成后,我们便可以在终端使用 bashdb 命令了,改命令典型用法如下:
[root@localhost ~]# bashdb --debug 脚本名
常用参数:
一、列出代码和查询代码类:
l 列出当前行以下的10行
- 列出正在执行的代码行的前面10行
. 回到正在执行的代码行
w 列出正在执行的代码行前后的代码
/pat/ 向后搜索pat
?pat?向前搜索pat
二、Debug控制类:
h 帮助
help 命令 得到命令的具体信息
q 退出bashdb
x 算数表达式 计算算数表达式的值,并显示出来
!! 空格Shell命令 参数 执行shell命令
使用bashdb进行debug的常用命令(cont.)
三、控制脚本执行类:
n 执行下一条语句,遇到函数,不进入函数里面执行,将函数当作黑盒
s n 单步执行n次,遇到函数进入函数里面
b 行号n 在行号n处设置断点
del 行号n 撤销行号n处的断点
c 行号n 一直执行到行号n处
R 重新启动当前调试脚本
Finish 执行到程序最后
cond n expr 条件断点
- 自适应的多列图文混排改进
- [译]Laravel 5.0 之运行环境及环境变量
- 90行代码,搞定日志监控框架
- Laravel Migrate 中的 Fresh 和 Refresh 命令
- 框架组件,究竟要不要自研?
- Nginx泛解析到子目录,自动判断有无public目录
- 撩妹必备,3行代码伪造出一个“好莱坞黑客”屏幕
- [译]Laravel 5.0 之事件调度程序 (定时任务)
- Laravel 5.5 在浏览器中渲染 Mailable 类型
- Laravel 5.4 及 5.5 中的全新字符串辅助方法
- Laravel 5.5 的自定义验证对象/类
- Laravel 5.5 为响应请求提供的可响应接口
- Laravel 5.5 为 Mailables 类型新增 theme 属性
- Laravel 5.5 的 “vendor:publish” 新增 provider 提示
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- TikTok 用什么算法传输并加密内容?
- 如何编写自己的Arduino库?
- 玩转 PhpStorm 系列(十一):编码风格篇
- 用Arduino剖析PWM脉宽调制
- 猿实战01——vue后台前端框架搭建
- qt编译程序无法在其他电脑上运行
- 一起学习PHP中断言函数的使用
- Jenkins实现自动化邮件发送踩坑记录
- sed 实用实例参考
- 微信小程序WXML页面常用语法(讲解+示例)
- Golang领域模型开篇,当Go遇上DDD
- Spring事务专题(五)聊聊Spring事务到底是如何实现的
- 深色模式适配指南
- 【Flutter 专题】97 仿网易新闻标签选择器
- 宋宝华:论Linux的页迁移(Page Migration)完整版