bash


28
六 11

bash中getopts的用法

getopts 用于解析命令行参数。
用法: getopts 选项字符串 名称 [参数]
举个例子:
一个a.sh内容如下
#!/bin/bash
usage() {
cat << -EOF-
Usage:
$0 -I interface -i ipaddr

-EOF-
exit 1
}
while getopts “I:i:” opt ; do
case $opt in
I) interface=$OPTARG ;;
i) IP=$OPTARG ;;
?) usage ;;
done

if [[ -z "$interface" || -z "$ip" ]] ; then
usage
else
ifconfig $interface $ip
fi

其中:
1. 选顶字符串中,后面跟”:”(冒号)表示后面要跟一个参数,这个参数应用空格与选项隔开;这个参数保存在OPTARG变量中。
2. 选项字符串以”:”开始,将打开“沉默错误报错方式”,不能识别的选择等,将不打印错误信息。
3. getopts 将使用三个变量:name, OPTIND, OPTARG,OPTERR.
OPTERR :如果OPTERR=0, shell将禁用“错误提示输出”,即使选项字符串的开头不是冒号。
OPTARG : 当getopts解析到“选项”时,将指定的参数保存到这个变量中。
OPTIND : 存储$* 位置参数中的位置,随着getopts的处理而被getopts修改。

  


5
一 11

Debian/Ubuntu上的Apache管理脚本

a2enmod, a2dismod, a2ensite, a2dissite

a2enmod/a2dismod  启用/禁用模块
a2ensite/a2dissite 启用/禁用虚拟站点

实际上,另外三个文件只是一个指向 a2enmod 的软链接。

Debian与Ubuntu上自带的Apache配置文件,是有自己独特的组织风格的(SUSE也独特),于是想应该有自己的“独特”的管理方式。man -k apache了一下,发现这四个“工具”。

用法非常简单,不跟参数运行,有会“向导”,也可直接跟模块名,或者site名。


25
十二 10

bash 4

1. 从 command_not_found_handle 说起

发现Ubuntu(以及openSUSE)都提供了一个非常“友好”的功能:当你输入错误的、不存在的命令的时候,系统(实际上是bash调用应用程序)会给出相当有“建设性的”建议:

hongchuan@ubuntu:~$ gvim
程序“gvim”已包含在下列软件包中:
* vim
* vim-gnome
* vim-tiny
* vim-gtk
* vim-nox
请尝试:sudo apt-get install <选定的软件包>

hongchuan@ubuntu:~$ mc
程序“mc”尚未安装。  您可以使用以下命令安装:
sudo apt-get install mc

不访先思考一下Ubuntu是如何实现这个功能的。

………………………………

是的,要“捕获”这个“错误”,通过bash无疑是最直接和高效的。

……………………

bash 4提供了一个command_not_found_handle“钩子”,当bash遇到“命令找不到”时,假如这个函数被“定义”,则command_not_found_handle $1 被执行。

Ubuntu的command_not_found_handle是这样定义的:

command_not_found_handle () {
if [ -x /usr/lib/command-not-found ]; then
/usr/bin/python /usr/lib/command-not-found — $1;
return $?;
else
if [ -x /usr/share/command-not-found ]; then
/usr/bin/python /usr/share/command-not-found — $1;
return $?;
else
return 127;
fi;
fi
}

/usr/lib/command_not_found是一个用Pythonp写的处理“错误”、并提供“建议”的程序。

随便提一下,我当前的Ubuntu版本是10.10(lsb_release -r ),bash是4.1.5(1)-release(echo $BASH_VERSION),当前系统提供的bash手册中,完全没有提到command_not_found_handle,而GNU网站上的bash手册中则有详细说明:Command-Search-and-Execution

2. Coprocesses (协进程/异步支持)

bash 4新增的内置命令coproc,用于在后台启动一个子shell,并将command放在子shell中执行,在后台执行的进程称作coprocess(协进程)。当前shell不需要等待command执行完成,于是有“异步”之功效。coproc command与command &某种程度上效果相同。

如何使用 coproc :

举个例子,执行  coproc  COMMAND,那么 coproc 会做几件事情:

1.在后台启动子shell,PID值放在变量 COPROC_PID中。
2.在子shell中执行COMMAND。
3.建立两个“双向管通”,管道的文件描述符为${COPROC[0]}和${COPROC[1]}。

这样,就建立了一个“父进程”与“子进程”之间进行通信的方式(管道)。其中 ${COPROC[0]} 连接着“协进程”的“标准输出”,${COPROC[1]}连接着 “协进程”的“标准输入”。父进程通过${COPROC[0]}读取“协进程”的输出,通过${COPROC[1]}向子进程发送数据。

应用举例:
#!/bin/bash
rw() {
while read line ; do
echo $line >> log.txt
done
}
coproc  rw
cat >&${COPROC[1]}

这个程序在前台启动一个cat,后台启动一个read的循环,读取前台的输入并将输入写入log.txt文件。

另一个:

#!/bin/bash
process_line() {
local line=$@
echo “$line”
}
coproc tail -f /var/log/messages
while read line ; do
process_line “$line”
done <&${COPROC[0]}

“协进程”逐行输出/var/log/messages新增的行,前台程序逐行处理。

实际上,在bash 3中,在没有提供coproc的环境里,也可以使用这样的“思想”,实现“shell的多进程编程”。

3. 关联数组

bash支持的新“数据结构”,类似于Python的字典,PHP的关联数组。就是
p[name]=’hongchuan’
p[age]=28
p[site]=’http://www.bsdmap.com’

可以使用字符串作为数组的下标,引用值时${p[name]},${p[site]},${p[age]}。

使用“关链数组”需要事先声明:
declare -A p

4. 大小写转换

declare -l hostname
declare -u  MAC

声明变量hostname为小写,当执行hostname=”WWW.BSDMAP.COM”时,hostname的值为www.bsdmap.com。
声明变量MAC为大写,当MAC=”00:1b:2f:4c:19:7e”,MAC的值为 00:1B:2F:4C:19:7E。

5.  前置0

bash3中,为了产生0021这样的数,我是这么做的

for vlan in {1..10}; do
vlan_id=`printf %4d vlan|sed ‘s/ /0/g’`
……
done

bash4中,可以这样:
for vlan_id in {0001..10}; do
……
done

6. mapfile (readarray)

将文件装入数组
mapfile aa < log.txt
将文件log.txt以行为单位赋值给数组aa
callback() {
echo ${aa[@]}
}

mapfile -C callback -c1 aa < log.txt

help mapfile 更多详情。这个mapfile功能相当适用。

7. 新增两个重定向简写符

&>> 和 |&

&>> 等价于 >> file 2>&1

|&     等价于 2>&1 |

更多新特性,见官方手册。

 

 

有用的链接:

http://www.gnu.org/software/bash/
http://wiki.bash-hackers.org/bash4

http://www.gnu.org/software/bash/manual/bashref.html


6
十一 10

在Shell中实现异步

在shell脚本中,如何实现异步的超时设置功能?

关键部分的代码如下:

            scp ../local_hw_oob_init.sh $i: &>/dev/null
            ssh $i /root/local_hw_oob_init.sh &>/dev/null &
            SSHPID=$!
            { sleep 60 ; kill -9 $SSHPID ; } &
            KILLPID=$!
            wait $SSHPID
            if [ $? = 0 ] ; then
                kill -9 $KILLPID
                echo -n "SSH_OK,"
            else
                echo -n "SSH_KILLED,"
            fi

首先,将主任务 TASK1 放到后台,记录 TASK1_PID;然后立即在后台再运行第二个任务 { sleep 60 ; kill -9 $TASK1_PID ; } 到后台,然后启动一个 waith $TASK1_PID 的指令。

假如在 60 秒内,TASK1 还没有返回,就会被第二个后台的任务 kill 掉,此时,waith 收到 TASK1 的退出状态,并返回。
假如在 60 秒内,TASK1 返回,则wait之后,紧跟着 kill 掉第二个任务。


10
二 10

系统调用exec和fork

Exec 与 fork 是 UNIX 中的两个系统调用,UNIX 程序利用它们来创建新的进程。

由一个进程产生 (spawn) 另一个进程 ,可能是进程产生后用新进程取代它,即exec;或者如何需要保留这个进程,那就复制一个进程,即 fork 。

举个例子:Getty 进程监测一个串行端口 (tty),提供了一个 “login:” 提示符,当用户输入登录名回车之后,getty的任务就完成了;它执行 (exec) 了 login 命令,当 login 检测密码输入正确之后,它执行 (exec) 登录 shell 。一旦用户启动另一个程序,shell 程序就会派生 (fork) 自己,并且这个复本将执行 (exec) 用户所要运行的任何程序。


7
二 10

大小写转换

将文件file.txt的小写字母转换成大写

1. 使用tr
cat file.txt | tr a-z A-Z
2. 使用sed
sed ‘y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/’ file.txt

sed的 y/source/dest/   Transliterate  the  characters  in  the  pattern space which appear in source to the corresponding character in dest.