通过交互方式接收用户参数,可以逐步引导用户使用脚本。
假设有个脚本test.sh需要用户输入参数param:
[root@node29 ~]# vim ./test.sh #! /bin/bash # 下文示例省略该行 param="" read param echo "The param you input is: ${param}"
以上是最基础用法,实际使用时,会发现很不好用。想要做得更友好,还需要关注细节。
下面先来了解一下read的常用参数。
[root@node29 ~]# vim ./test.sh param="" read -p "Please input param: " param echo "The param you input is: ${param}"
[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这个参数对于增强脚本的易用性非常有帮助,例如,有以下两个需求:
那么,就需要使用-e参数,再配合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 中。
现在有这样一个需求:脚本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命令,例如:
[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是一个千行以上脚本文件,以上报错存在两个问题:
也就是说,这样的报错信息并不友好,会误导使用者,也不利于定位问题。
下面稍作优化:
[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
执行结果如下:
两方面改善:
利用脚本的调试信息,还可以实现其它功能,例如,打印调用栈……
最后,如果shell脚本比较复杂,在debug时,建议使用bashdb debug,以定位问题。