工具

Shell脚本易用性编程

0 条评论 工具 Python Shell FairyFar

一、Shell交互式编程

通过交互方式接收用户参数,可以逐步引导用户使用脚本。

1.1 使用read接收用户输入参数

假设有个脚本test.sh需要用户输入参数param

[root@node29 ~]# vim ./test.sh 
#! /bin/bash   # 下文示例省略该行

param=""
read param
echo "The param you input is: ${param}"

以上是最基础用法,实际使用时,会发现很不好用。想要做得更友好,还需要关注细节。
下面先来了解一下read的常用参数

1.2 read常用参数

-p:显示提示符,例如:

[root@node29 ~]# vim ./test.sh 
param=""
read -p "Please input param: " param
echo "The param you input is: ${param}"

-d:表示delimiter,即定界符,一般情况下是以IFS为参数的间隔,但是通过-d,我们可以定义一直读到出现执行的字符位置。
-t:用于表示等待输入的时间,单位为秒,等待时间超过,将继续执行后面的脚本。
-e:在获取用户输入的时候,对功能键做了处理。例如:

[root@node29 ~]# vim ./test.sh 
param=""
read -e -p "Please input param: " param
echo "The param you input is: ${param}"

带-e参数的区别,需要自己体会,分别执行上面带-e和不带-e的两个脚本,然后在输入参数时使用上下方向键,看看有什么不同。
带-e后,我们会发现,可以像在shell命令行中一样,翻查历史命令(history),history后文再具体说。

-e这个参数对于增强脚本的易用性非常有帮助,例如,有以下两个需求:

  1. 某个输入参数是文件或者目录的路径,在shell命令行中,我们经常会使用Tab键自动补齐,那么-e参数可以实现参数输入时的自动补全功能。
  2. 用户输入参数时,可以翻查之前的历史输入记录。

那么,就需要使用-e参数,再配合history来实现。

二、history

2.1 history命令

在命令行手动输入过程中,一个很常用的功能就是,快速翻查之前已经输入过的命令,以加快命令输入过程。
bash有完善的历史命令,满足该需求,使用简单方便。
显示命令history:

[root@node29 ~]# history 
  1  set -o history
  2  set -o history 2> /dev/null
  3  read -p "Please input param" param
  4  vim ./test.sh

其它参数:
-c:清空历史命令。
-w:把缓存中的历史命令写入历史命令保存文件中。如果不手工指定历史命令保存文件,则放入默认历史命令保存文件~/.bash_history 中。

2.2 定制脚本自己的history

现在有这样一个需求:脚本test.sh接收用户交互式输入参数,且用户可以翻查之前输入过的参数历史记录。
代码如下:

[root@node29 ~]# vim ./test.sh 
# 设置history文件,以及size:
HISTFILE=~/.test_history
HISTSIZE=1000
HISTFILESIZE=1000

# 启用history
set -o history

param=""
read -e -p "Please input param: " param
echo "${param}" >> ${HISTFILE}
echo "The param you input is: ${param}"

运行以上test.sh脚本,任意输入参数,再次运行test.sh脚本,然后使用history快捷键,就可以翻查到之前的输入记录了。

三、显示进度

对于一个执行时间比较长的操作,如果可以动态显示处理进度,将有效提高交互的友好性。
以下是动态显示处理进度的最基本代码:

[root@node29 ~]# vim ./test.sh 
for ((i=0; $i<=100; i+=1)); do
    printf "progress: %d\r" $i
    sleep 1
done
echo ""

Python 2.7进度显示:

[root@node29 ~]# vim ./test.py 
#! /usr/bin/env python
#coding: utf-8

from __future__ import print_function
import sys
import time

for i in range(0, 100):
    print('progress: %d' % i, end='\r')
    sys.stdout.flush()
    time.sleep(1)

Python 3进度显示(比Python 2.7要简洁):

[root@node29 ~]# vim ./test.py 
#! /usr/bin/env python
#coding: utf-8

import time

for i in range(0, 100):
    print('progress: %d' % i, end='\r', flush=True)
    time.sleep(1)

四、shell脚本执行Python脚本

shell脚本中,可以直接执行Python命令,例如:

[root@node29 ~]# vim ./test.sh 
python -c "print 'abc'"

也可以直接执行Python脚本,例如:

[root@node29 ~]# vim ./test.sh 
python ./test.py 

现在有这么一个需求:已有shell脚本test.sh,已经开发了很多功能,但是有个新功能需要处理一个大文件(文件大小几十GB以上),直接使用shell脚本处理效率太低,想用Python来实现,但是,这个Python的处理脚本内容较长,不适合使用“直接执行Python命令”方式。为了便于后期维护脚本维护,不想因为这个功能再增加一个Python脚本文件。
无论哪种脚本文件,最后都是文本文件,因此,我们可以“使用一个脚本生成另一个脚本”,以达到多个脚本内容合成在一个文件的效果。此方法同样适用于其它不同脚本语言之间的交叉使用。
范例如下:

[root@node29 ~]# vim ./test.sh 
# Python脚本内容
PYTHON_TXT="\
#! /usr/bin/env python
#coding: utf-8

import sys
import os

if __name__ == '__main__':
    file = sys.argv[1]
    with open(file, 'r') as r_f:
        for line in r_f:
            print(line)
"

# 生成Python脚本文件
echo "${PYTHON_TXT}" > ./test.py

# 执行Python脚本
python ./test.py big_file.txt

五、友好的报错提示

现在有一个示例脚本,其中big_files文件不存在,

[root@node29 ~]# vim ./test.sh 
show_file_list()
{
    echo "begin"
    file_list=`ls ./big_files`
    if [ "$?" != 0 ]; then
        echo "Invalid file list!"
        return 1
    fi
    echo "file_list is: ${file_list}"
}

show_file_list
执行以上脚本,效果如下:
执行效果
假设test.sh是一个千行以上脚本文件,以上报错存在两个问题:

  1. 虽然是正常的报错,但是会让人误以为脚本有BUG;
  2. 仅从报错信息,无法知道是哪一行报的错。

也就是说,这样的报错信息并不友好,会误导使用者,也不利于定位问题。
下面稍作优化:

[root@node29 ~]# vim ./test.sh 
my_tip()
{
    echo "${FUNCNAME[1]}(), line ${BASH_LINENO[0]}: $*"
}

show_file_list()
{
    echo "begin"
    file_list=`ls ./big_files 2> /dev/null`
    if [ "$?" != 0 ]; then
        my_tip "Invalid file list!"
        return 1
    fi
    echo "file_list is: ${file_list}"
}

show_file_list

执行结果如下:
执行效果
两方面改善:

  1. 隐藏了命令原有的报错信息,仅显示脚本期望的报错信息;
  2. 从报错信息,可以知道是脚本哪里报的错。

利用脚本的调试信息,还可以实现其它功能,例如,打印调用栈……
最后,如果shell脚本比较复杂,在debug时,建议使用bashdb debug,以定位问题。


Code::Blocks 20.03 CentOS 7离线安装

0 条评论 工具 IDE FairyFar

一、安装环境

操作系统:CentOS 7 64位
ssh工具:Xmanager Enterprise 5
注:无需登录到Linux桌面,使用Xshell ssh登录到命令终端即可。Code::Blocks启动后界面将在Windows环境中显示,但是运行在Linux里。

二、简介

Code::Blocks自从2017年发布17.12版本,时隔三年终于更新版本了——20.03。

三、安装

本文以6位系统为例,如果安装32位Code::Blocks,请下载相应的32位版本。
以下rpm包,除了Code::Blocks相关包,其它包可以到rpmfind.net搜索。
请按以下顺序安装:

[下载] rpm -i wxBase-2.8.12-20.el7.x86_64.rpm
[下载] rpm -i wxGTK-2.8.12-20.el7.x86_64.rpm
[下载] rpm -i codeblocks-libs-20.03-1.el7.x86_64.rpm
[下载] rpm -i libXaw-1.0.13-4.el7.x86_64.rpm
[下载] rpm -i xterm-295-3.el7.x86_64.rpm
[下载] rpm -i codeblocks-20.03-1.el7.x86_64.rpm
[下载] rpm -i codeblocks-devel-20.03-1.el7.x86_64.rpm
[下载] rpm -i codeblocks-contrib-libs-20.03-1.el7.x86_64.rpm
[下载] rpm -i gamin-0.1.10-16.el7.x86_64.rpm
[下载] rpm -i codeblocks-contrib-20.03-1.el7.x86_64.rpm

四、运行

在Xshell中,直接执行“codeblocks”,IDE节点将在本地Windows中启动。


Shell调试工具bashdb

0 条评论 工具 Shell Linux FairyFar

一、什么是bashdb

bash是Unix/Linux操作系统最常用的shell之一,bash脚本经常用于编写批处理工具。
一般地,编写bash脚本时,也需要进行debug,通常会加一些print或者echo进行脚本调试,调试结束后还需要删除这些调试代码。
实际上,linux环境下也有类似gdb的工具用于调试bash脚本。这其中,bashdb相对来说更易于使用,使用方法也十分类似gdb。
bashdb开源,基于GLPv2协议。

二、编译安装bashdb

2.1 bashdb项目主页

bashdb.sourceforge.net

2.2 下载源码

https://sourceforge.net/projects/bashdb/files/bashdb/
需要注意的是,不同版本的bashdb对bash版本有要求,bashdb 5.0以上版本要求安装环境的bash版本也是5.0以上版本。

[root@node29 ~]# bash --version 
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

2.3 编译安装

本文以bashdb-4.4-1.0.1版本为例,其它版本类似。

[root@node29 ~]# tar -xf bashdb-4.4-1.0.1.tar.bz2 
[root@node29 ~]# cd bashdb-4.4-1.0.1 

[root@node29 bashdb-4.4-1.0.1]# ./configure 
[root@node29 bashdb-4.4-1.0.1]# make 
[root@node29 bashdb-4.4-1.0.1]# make install 

[root@node29 bashdb-4.4-1.0.1]# bashdb --version
bashdb, release 4.4-1.0.1

三、bashdb使用示例

以下以一个简单调试为例,演示bashdb使用方法,详细内容请参考bashdb用户手册。

[root@node29 CORED]# bashdb --debug /root/tools.sh
bash debugger, bashdb, release 4.4-1.0.1

Copyright 2002-2004, 2006-2012, 2014, 2016-2018 Rocky Bernstein
This is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.

Reading /root/tools.sh done.
(/root/tools.sh:11):
11:    LOG_DIR="./tools_log"
bashdb<0> b 1095
Breakpoint 1 set in file /root/tools.sh, line 1095.
bashdb<1> c
Breakpoint 1 hit (1 times).
(/root/tools.sh:1095):
1095:        func_sel="$1"
bashdb<2> n
(/root/tools.sh:1098):
1098:        lioc_dmc_prep
bashdb<3> print $func_sel
1
bashdb<4> q
bashdb: That's all, folks...

四、more…

如果想要进一步使用IDE调试bash脚本,可以在图形界面下,安装DDD。
详情参考:http://www.gnu.org/software/ddd/


git实用命令

0 条评论 工具 git FairyFar

clone仓库

git clone http://fairyfar@xxx.xxx.xxx.xxx:9090/scm/ofs/ofs.git

查看log

git log  #查看当前分支log
git log origin/feature/XXX  #查看远程分支log

查看分支

git branch  #仅显示本地分支
git branch -a  #显示全部分支,含远程分支。
git branch -vv  #显示本地分支与远程分支对应关系

从远程分支创建一个本地分支

git checkout -b 本地分支名称 origin/feature/XXX

临时checkout指定版本

已知commit id,可以临时checkout该提交。

git checkout commit_id

当不再需要该临时本地分支时,直接checkout到其它本地分支即可。

删除本地分支

git push origin 本地分支名称:feature/XXX  #假设希望的远程分支全称为:origin/feature/XXX

本地分支关联到远程分支

git branch --set-upstream-to=origin/remote_branch  本地分支名

切换到已存在的本地分支

git branch -D 本地分支名称  #-D是强制删除,会忽略本地修改。

从本地分支创建一个远程分支

git push origin 本地分支名称:feature/XXX  #假设希望的远程分支全称为:origin/feature/XXX

这也是push本地commit到远程分支的方法。

删除远程分支

git push origin --delete feature/XXX

合并(merge)远程分支到本地分支

git merge remote_branch  #合并可能会产生冲突,需要解决冲突。

合并(merge)分支时忽略行结尾符差异

git merge --no-ff -Xignore-space-at-eol some-branch

-Xignore-space-at-eol这个参数会忽略文件结尾符比较。

修改本地分支log(尚未push到远程分支)

git commit --amend

合并本地分支的多次提交(commit)

本地开发过程中,可能commit多次,但是后来我们希望将多次commit合并为一个再push到远程分支,表现为log中的多个记录合成一个。
首先,

git log  # 查看log,假设commit_id1 和 commit_id2希望合并,那么我需要记住commit_id2后面的commit_id3。

然后,

git rebase -i commit_id3

编辑时,按照命令说明修改。保存退出。

临时保存本地修改

git stash  #将本地修改(尚未commit)临时保存到stash中
git stash list  #列出所有stash
git stash pop  #恢复stash
git stash apply  #应用stash,会从stash中清除。
git stash show -p SN  #显示索引号为SN的stash记录的diff

撤除本地没有提交的修改

git checkout 要撤销的文件或文件夹

删除untrack文件和文件夹

git clean -df  #删除当前目录下untrack文件和文件夹,不包括.gitignore中指定的。
git clean -f  #删除当前目录下untrack文件,不包括文件夹和.gitignore中指定的。
git clean -xdf  #删除当期目录下的所有untrack的文件和文件夹

撤销git commit

已经commit到本地分支,尚未push到远程分支。

git reset --soft HEAD^  # HEAD^是上一个版本,也可以写成HEAD~1。
git reset --hard HEAD^  # hard删除工作空间改动代码,撤销git add。

撤销git push

已经push到远程分支,有两种方式撤销。

方法1:

先使用上述撤销commit的方法,然后重新提交。

方法2:

git revert commit_id。

git push不输入密码

git config credential.helper store

这样仅在第一次push时输入密码,下次就不用输入了。

git合并指定的commit

有时我们不希望merge某个分支的全部变更,只是想把某次commit合并过来。

git cherry-pick commit_id  # 合并指定的某次commit

git commit模板

每次commit代码时,都需要按格式填写commit message,因为格式固定,所以我们可以使用commit message template。
创建模板:

vi ~/.git_commit_template

编写模板,例如:

[KEYWORDS ] ci-
[TO  SOLVE] X100-
[TEST SUGGESTION]

然后vi ~/.gitconfig,编辑以下内容:

[commit]
template = ~/.git_commit_template

这样,每次git commit会自动应用上述模板。

删除远程分支后,本地还是能看到远程分支名。

git branch -a  # 还是可以看到远程分支名称,但是远程分支已删除。
git remote show origin  # 可以查看远程地址,远程分支,还有本地分支与之相对应关系等信息。
git remote prune origin  # 本地删除远程不存在的分支

gdb常用命令

0 条评论 工具 gdb FairyFar

加载core

gdb oSan core.XXXX

调试带命令行参数程序

gdb --args ./prog arg1 arg2

使用源代码对照调试窗口

在有源代码的环境下,在gdb调试状态,使用ctrl + X,A可以切换到源代码对照调试窗口。
gdb
此时会分上下屏显示源代码和命令窗口,使用以下命令可以将设置当前键盘操作窗口:

(gdb) focus src
(gdb) focus cmd

冻结当前线程之外其它线程

在多线调试环境时,有时为了单步调试时,避免被其它线程打断,可以冻结其它线程:

(gdb) set scheduler-locking on

解冻其它线程:

(gdb) set scheduler-locking off

数据断点

为了跟踪某块内存在是哪里被修改了,可以设置数据断点,例如,假设希望在0x12345678这个4字节的内存区域在被修改时中断,可以:

(gdb) watch *(int*)0x12345678

那么,当上述4字节内存区域被修改时,触发中断。

条件断点

当指定的条件满足时break。例如,假设希望osan_lpc_submit_io函数在满足p_io->lunset_id为0时中断:

(gdb) b osan_lpc_submit_io if p_io->lunset_id==0

格式化显示

按十六进制打印变量val:

(gdb) p/x val

打印数组多个元素的值:

(gdb) p *array@len

按二进制打印变量val:

(gdb) p/t val

格式化打印结构体:

(gdb) set print pretty on

反汇编

(gdb) set disassembly-flavor intel  # 使用intel格式,默认为AT&T格式。
(gdb) disassemble main  # 反汇编
(gdb) disassemble /m main  # 反汇编与源码对照

gdb启动时执行调试命令

例如,gdb启动时给main函数打断点“b main”:

gdb ./test -ex "b main"

gdb启动时执行调试命令脚本

假设有一个脚本文件~/my_gdb,内容如下:

b main
b start_fun
r

gdb启动时执行该脚本

gdb ./test -e ~/my_gdb

gdbinit(gdb初始化脚本)

分三个级别:

/etc/gdbinit:
System-wide initialization file. It is executed unless user specified GDB option "-nx" or "-n".
    
~/.gdbinit:
User initialization file. It is executed unless user specified GDB options "-nx", "-n" or "-nh".
    
./.gdbinit:
Initialization file for current directory.

gdbinit应用举例

set scheduler-locking on和set scheduler-locking off这样的gdb指令有点长,如果经常使用不太方便,我们可定义一个短一点的命令替代之。
编辑~/.gdbinit文件,增加内容:

def lkon
    set scheduler-locking on
end
def lkoff
    set scheduler-locking off
end

这样以后就可以使用lkon和lkoff代替前面的长命令了。

查看所用线程堆栈信息

(gdb) thread apply all bt

结果输出到文件

(gdb) set logging file <file name>
(gdb) set logging on
(gdb) 。。。。。。
(gdb) set logging off

gdb非交互式

gdb -q --batch --ex "set height 0" --ex "bt" [core] [exe]

不分页

(gdb) set height 0 

打印长文本

(gdb) set print elements 0 
(gdb) show print elements 

Dump内存区到文件:

(gdb) dump binary memory <文件名> 内存起始地址 内存结束地址

捕捉异常

gdb> catch throw
catch catch

当异常抛出时中断。

加速gdb调试so时的加载速度

如果gdb index很大,那么gdb调试时,load时间会很长,那么可以在编译时使用以下命令,把gdb index追加到.so文件,这样在gdb调试时,减少了index计算时间。

gdb-add-index libtest.so

strip

去除release版本程序的符号库。

strip sql/mysqld