最常用的也是最容易忘记的Shell知识
Q: 如何取得当前脚本的名字?
A: declare me=`basename $0` (注意,`是和~同一个键的那个字符)。这样在脚本中需要用到当前脚本名字的地方用$me来代替,比如usage信息的时候。
Q: 如何取得当前脚本所在的目录?
A: declare parent_dir=$(dirname $(readlink -f $0))
Q: 如何取得当前用户的用户名
A: declare current_user=$(id -un)
Q: 如何取得当前用户的home目录
A: declare current_user_home=$(dirname ~)/`basename ~` 不知道为什么,脚本中直接用~来表示当前用户的home目录不行。所以只好用这种办法了。
Q: 如何定义常量?
A: readonly version="V1.0"
Q: 如何引用变量var?
A: 当然是$var了,不过如果想把var中的值和其他字符拼起来的话,比如$var_log,shell会吧var_log当成一个变量。这时可以用${var}_log
Q: Bash中支持指针吗?
A: 支持。
假设current_user=jewes,同时定义var=current_user,${!var}的值就是jewes。它先对var求值,然后将var的值作为一个变量再求值。
Q: 如何定义函数
A: 函数的定义方式如下。如果是在ksh中,括号可以不要。
- function foo()
- {
- # function body
- }
Q: 如何使用if-else?
A: bash中的if条件的格式是为:
- if[ condition ]; then
- # do something
- else
- # do something else
- fi
注意:
- condition和[]间必须要有个空格。
- 如果是ksh,要写成if [[ condition ]], bash也支持这种写法。
Q: 函数可以返回一个字符串吗?
A: 不能直接return "string"。但可以用如下方法:
在函数内部用echo 来输出字符你想要返回的字符串,比如
- function get_name()
- {
- echo jewes
- }
然后在调用函数的地方写成$(get_name),这样就能得到函数内容输出的字符串。
Q: 如何给函数传递参数
A: 直接将参数写在函数名称的后面,就像执行shell命令一样,如果参数中有空格,需要将其用引号包起来。这个点很重要,如果没有引号,shell会认为是几个参数,比如"hello world"是一个参数;而hello world 是两个参数。
Q: 如何取得函数的参数?
A: $0表示第一个参数,以此类推。$#表示总共有几个参数传递进来。$*表示所有传递进来的参数。
Q: 如何定义局部变量?
A: local id=jewes,不过local只能在一个方法中定义。貌似ksh不支持local
Q: 如何取得函数的返回值
A: 用$?表示上一个函数或者shell命令的返回值。如果你的代码根据不同的返回值执行不同的代码,建议先将$?赋值给一个变量,比如:
- func
- if [ $? -eq 0 ]; then
- # do something
- echo $? ####-->>> 如果你以后再这句话之前加了新的代码,$?就不再是func的返回值了,因此建议在func执行完后立刻保存其返回值到某个变量中。
- else
- # do something else
- fi
Q: 如何判定一个文件是否存在?
A: Sample code
- if [ -f $filename ]; then
- # do something when file exists
- else
- # do something when file doesn't exist
- fi
注意:
- []和里面的条件直接必须有空格。比如 [ -f $filename] 就不对了。
- 如果要看一个目录是否存在用-d
- 如果要判定一个文件不存在,在-f 前面加上!
Q: 如何判定一个目录是否为空?
A: Sample code
- if [[ "$(ls -A $WSPACE_ROOT/$wspacename)" ]]; then
- echo "folder not empty"
- fi
Q: 如何输出有颜色的字符
A: Sample code
- function echo_green()
- {
- COLOR='\033[01;32m' # green color
- RESET='\033[00;00m' # normal color
- MESSAGE=${@:-"${RESET}"}
- echo -ne "${COLOR}${MESSAGE}${RESET}"
- }
- cho_green "A green message"
其中,32m表示绿色, 31m表示红色,34m表示蓝色,33m表示黄色。
Q: 如何提示输入密码并在屏幕上显示为*
A: Sample code
- function enter_password()
- {
- local prompt=$1 # prompt message
- unset user_typed_password
- while IFS= read -p "$prompt" -r -s -n 1 char
- do
- if [[ $char == $'\0' ]]; then
- break
- fi
- prompt='*'
- user_typed_password+="$char"
- done
- echo "" # force a carriage return to be output
- }
- enter_password "Tell me your bank account password:"
其中,密码将被保存到user_typed_password,调用这个方法后就可以从其中取得用户输入的密码。
Q: 如何记录某台命令执行了多长时间?
A: Sample code
- starttime=$(date +%s)
- # some time consuming command
- endtime=$(date +%s)
- timediff=$(( $endtime - $starttime ))
timediff中保存的是两个时间差,以秒为单位。
Q: 如何进行算术运算?
A: 举个例子
local index=1
index=$(( $index + 1 ))
+可以是+-x/%
Q: 如何在指定的列输入文件
A: 假如你要在屏幕上输出格式相对整齐的表格数据,希望在指定的列输出文字,可以用:
echo -ne "\033[65G"
echo "[ OK ]"
表示在第65列输出[ OK ]
Q: 如何生成公私钥?
A: ssh-keygen -qC "Some comments" -f <private key filename> -N ""
这个命令将产生一对密钥对并保存到指定的文件中,公钥以.pub文件结尾。这个命令不需要人工干预。因为在我的环境下,脚本要给用户自动生成密钥对。
Q: 在ssh到远程机器的时候,如何自动接受远程机器的HostKey?
A: 如果你第一次ssh到远程机器,ssh会提示你是否接收远程机器的Key。在脚本环境下,它可能会使你的脚本停下来。可以在ssh的时候指定下面两个参数:
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null 第一个参数是让ssh自动接受远程机器的key,后面一个参数是不让其保存默认的knownhosts文件中,这主要是避免远程机器改变了key,脚本就又不工作了。
Q: 如果远程机器只支持telnet,如何自动登录到机器上执行命令
A: 用expect。
Q: 如何将一个函数放到后台运行?
A: 就像shell命令一样,在脚本中调用一个函数的时候只要在后面跟上 & ,shell将开启一个新的进程来执行这个函数,新的进程的id可以通过$!来取得。
Q: 如何将一些公共的函数放到一个文件中以便被多个脚本重用
A: 建个新文件比如common_func.sh,把常量和工具函数都放到里面。
在想要用这些公共函数的Shell脚本比如main.sh中,只需加上". ./common_func.sh"就可以了,前提是它们是在同一个目录里面。
上面的写法在当前目录下运行这个脚本是没有问题的,如果在别的目录下运行main.sh会遇到找不到common_func.sh这个文件。解决办法是在脚本里面先找到当前脚本的父目录,然后用绝对路径来引用common_func.sh这个文件(假设它们之间的相对位置的固定)。参加前面的FAQ如何找到当前脚本的父目录
Q: 有什么方法能方便地读取脚本的参数吗?
A: 用getopts。
举个例子:我们的脚本是myscript.sh, 支持一个带参数的选项-w,一个不带参数的选项-h。
- while getopts hw: options 2> /dev/null
- #注意w后面有个: 表示它后面必须要有一个参数。h后面没有冒号,表示它后面不能有参数。
- do
- case $options in
- h) print_usage_and_exit;; #必须要两个;;
- w) val=$OPTARG;; #取得跟着-w后面的值
- \?) print_usage_and_exit;;
- esac
- done
注意这种方法的选项只能是一个字母。
Q: Shell有什么单元测试的框架吗?
A: shunit2,简单看了下觉得应该不错,不过没有用到我的脚本中。