这天同事问了一个问题,shell 中如何重复显示指定数目的字符?例如显示 72 个等号(=)作为一个分界线。脑子里马上想到 perl 和 python 中这个实在是太容易了:
$ perl -e "print '=' x 72"
$ python -c "print '=' * 72"
但是 shell 中可没有这样的语法,想了半天,只想到一个利用 printf 命令结合 sed 的做法:
$ printf "%*s" 72 '' | sed 's/ /=/g'
"%*s" 这个 format string 后面的第一个参数是字符串显示宽度,第二个参数才是真正要显示的字符串(这里是空串),与 C 语言中的 printf 类似。
虽然这个 printf 有点妙,不过后面跟了个 sed 才算解决问题,作为一种脚本语言,Shell 有时让人觉得实在是太弱了。应该有更好的解法……
Jan 4, 2009 at 4:14am | 2 Comments
Tags: shell
用 iozone 跑出来的文件系统性能数据,格式示例:
# file-size initial-read
1k 3029.832
2k 3010.015
4k 3223.711
8k 3303.412
16k 3488.440
简单任务:将 Nk 替换成 N*1024 的结果,m 和 g 依此类推。目的是要对处理之后的结果用 gnuplot 作图。
用正则表达式很容易匹配目标文本,但可惜不能直接计算。我的解法是先将整个文本替换成一个可执行的 shell 脚本 - 第一行插入 "cat << EOF",最后一行添 "EOF",中间的 k, m, g 替换成 "$((..))",然后用 shell 解释执行,利用 shell 进行计算。有点土,不过至少可以方便批量处理。
sed -e '1i\cat << EOF' \
-e 's/\b\([0-9]\+\)k\b/$((\1*1024))/g' \
-e 's/\b\([0-9]\+\)m\b/$((\1*1024*1024))/g' \
-e 's/\b\([0-9]\+\)g\b/$((\1*1024*1024*1024))/g' \
-e '$i\EOF' \
iozone-out.txt | bash
将 sed 表达式写到一个单独的 .sed 文件,可方便批量处理。再完善一点就先把 $ 和 ` 转个义。
for f in *.txt; do sed -f x.sed $f | bash > $f.new; done
谁有更好的解法?
Nov 24, 2008 at 9:50pm | 0 Comments
Tags: script and shell
学无止境。很多以前觉得正确的、好的方法,随着知识的积累、见识的长进,可能会变得不再正确或者最好。这在我脚本编程、工具的使用方面犹为明显。我计划将这作为一个主题,将平时遇到的“简单任务”和解法记录下来,日后有更好的方法再更新。
简单任务 - 将多个连续的空行合并成一个空行。来自一个实际问题:打印一个很长的 RFC 文档,希望能省一些纸张。
很久以前就遇到过这个问题,因为发现 sed 不能匹配 '\n',当时的解法是写了个 shell 脚本处理。Vim 可以用 '\n' 匹配换行符,解决方案是:
vim '+%s/^\n\+/\r/g' +x rfc.txt
考虑空行上可以有空白字符的话,还能再完善一点:
vim '+%s/^\(\s*\n\)\+/\r/g' +x rfc.txt
这是目前的“最佳应法”,欢迎提供更佳的解法。
特别鸣谢:刚开始缺少 '^' 怎么都不能成功,经老婆指正方才解决。你怎么也想不到,我老婆作为一个文科生,竟然能把 vim 使得出神入化,还在她们公司内部做了一个四十分钟的讲座!
Nov 23, 2008 at 9:12pm | 3 Comments
Tags: script and shell
公司有台 Build server, 我们都可以在上面用一个 root 权限来执行的脚本为自己分配一个 8G 的空间来用,但可惜我来得晚,空间已经被瓜分殆尽,只剩下 4.77G,更要命的是,这个脚本只分 8G,它发现空间不够时不会分配剩余的空间,而是直接报错退出。
今天下午我就琢磨怎么把这 4.77G 拿来自己用了,首先想到的当然是看那个分配空间的脚本,但是,那个脚本 cb_dir.sh 的权限设置是
-r-S--x--x 1 root root
我只能执行,不能 read。不甘心啊,在硬盘上看了一会,我发现它的一个备份文件,并且是可以读的,哈哈!
这是个 ksh 脚本,判断空间大小的代码:
#
# check bpool avaialble space
#
QUOTA_SIZE=8
AVAIL=`zfs list -H -o available $POOL`
echo $AVAIL | grep G >/dev/null 2>&1
(( $? > 0 )) && {
echo "Not enough space. Directory is not created."
exit 1
}
AVAIL_SIZE=`echo $AVAIL | tr -d G`
(( $AVAIL_SIZE < $QUOTA_SIZE )) && {
echo "Not enough space. Directory is not created."
exit 1
}
写得不是那么简洁 :P,下面切入正题了:脚本开始没有设置 PATH,并且用到的命令都没有带绝对路径,有机可乘!
$ cat > /tmp/tr << EOT#!/bin/ksh
print 9
EOT
$ chmod +x /tmp/tr$ export PATH=/tmp:$PATH
然后执行那个脚本,哈,4.77G 划到我的名下了。
如果脚本开始设置好 PATH,或者命令全都用绝对路径 (root 嘛,再怎么小心都不为过),就不会有这个漏洞了。这个例子可以拿去教育那些把“.”放到 PATH 中的人 ;)
Sep 20, 2006 at 12:04am | 0 Comments
Tags: shell
周五下班前遇到一个怪问题,gnome-session 突然启动失败,日志文件里也看不出问题根源。今天在家决心把它调试出来。过程很是费劲,首先要用 vpn 连到公司网络,然后 SecureCRT 登陆上公司机器,安装 x11vnc,还得一个 X Server 才能在公司机器上运行 X 程序,不想折腾 X-win32 或别的了,自己 VMware 里面也装了 ubuntu,启动 X 然后从里面 ssh -X 过去就可以启用 X11 Forwarding了。光这些还不行,x11vnc 是启动不了,因为用户还没有登陆,需要设置 gdm 自动登陆用户。运行 /usr/sbin/gdmsetup 可以设置自动登陆,后来发现还可以直接修改 /etc/gdm/gdm.conf-custom,增加这样一段(后面注释掉的三行是超时自动登陆的设置):
[daemon]
AutomaticLoginEnable=true
AutomaticLogin=username
#TimedLoginEnable=true
#TimedLoginDelay=10
#TimedLogin=username
然后重启 gdm,不停用 w 命令观察,果然看到用户正在自动登陆,执行的命令是 /etc/gdm/Xsession,接下来应该就是失败了弹出对话框了,因为正常的应该是执行到 /usr/bin/gnome-session。打开 /etc/gdm/Xsession 看了看,太多没耐心看,在第一行 #!/bin/sh 后面加 -x 参数,然后在第一条命令前加
exec 1>/tmp/x.log
exec 2>&1
再重启 gdm,看 /tmp/x.log,看到
++ '[' '!' -d /etc/X11/Xsession.d ']'
+++ /bin/ls -F --color /etc/X11/Xsession.d
++ for F in '$(ls $1)'
++ expr '^[[0m^[[0m20xorg-common_process-args^[[0m' : '[[:alnum:]_-]\+$'
++ for F in '$(ls $1)'
++ expr '^[[0m30xorg-common_xresources^[[0m' : '[[:alnum:]_-]\+$'
...
哈哈,看到那些 ^[[0m 控制字符我就明白了,问题在于 ls 被 alias 成了 '/bin/ls -F --color' 了,颜色控制字符搞的鬼。想不到大牛也能犯这种低级错误啊 :)。再读了一下,脚本会读取 /etc/profile 和 $HOME/.profile,如果它们中任一个设了那种 --color 的 alias,就会导致这个失败,我的情况正好是 /etc/profile 中设置了,很土吧?
Fix 就很简单了,把 ls 改成绝对路径 /bin/ls 就行了。
想到解决过程的麻烦,我决定到 bug 数据库里面去报一报,查了一下,果然有人遇到过,并且报告了,但没有解决到点子上,于是心情很爽的添加 comments。
Bug #48876: gnome-session fails when "alias ls='ls --color'" in .profile
Bug #45951 (duplicate)
Aug 6, 2006 at 5:30am | 0 Comments
Tags: shell and ubuntu