Bash script to find the jar package which contains the class you wanted

Many many time, ClassNotFoundException.

To find the necessary jar files is annoying trivial thing but I have to handle. I have to search in lots archives and check it manually.

But now, script is beautiful.

#!/bin/bash
# Last Change: 2010-07-27 11:57:39
# Author: a@yegong.net

export PATH="$HOME/bin:/bin:/usr/bin"

class_name=$1

find -name "*.jar" | while read file;
do
  unzip -l "$file" | sed -r -e 's/^[^#].* (.*class)/\1/' \
    -e 's/\//./g' -e 's/\.class//' | grep "$class_name" \
    | while read class;
  do
    echo "$file" : "$class"
  done
done | grep -r --color "$class_name\|[^/]*jar"

on July 27th, 2010 | 1 Comment »

sed script: find all occurs of keywords with context

今天需要在java代码中检查被try…catch…忽略的异常,一个文件一个文件的搜索太过繁琐且容易出错。把所有改动加入到svn的某个changelist中,用svn diff输出所有的改动。然后vim+grep。

顺便想像一下sed可以怎么做。

以前对sed的利用完全停留在sed s/abc/123上面,偶尔用到n p几个命令。今天查manual才知道,原来sed把/PATTERN/称做一个Address,而地址还可以嵌套成Range。例如/PATTERN/,+4就是指从PATTERN开始连续的5行。而命令p等,是可以应用在一个区间后的。

所以

cat ~/XMLPROD-32.diff | sed -n -e '/catch/i===' -e '/catch/,+2p'

on May 11th, 2010 | No Comments »

Handle space and newline in shell script

Many many times, we need to deal with the utilities output. There’s always a problem troubling me. For example, I want to handle the output of ls command when some filename contains space in it. It’s easy for one file result. But things get more complex when I need to travel through all the files. Because the results will split to array by not only the newline sperator but also space. Google told me nothing maybe for the wrong keywords. But now, I figure out one,

result=`svn st | sed -r -e 's/^\? +//' -e 's/ /{{SPACE}}/'`
for file in ${result[@]}
do
    file=`echo "$file" | sed 's/{{SPACE}}/ /'`
    rm -rf "$file"
done

Actually, it’s a part of my script svn.rmrevert. It revert the whole working copy and remove the files not in the repository.

on April 21st, 2010 | No Comments »

我是shell脚本控

最近两个月都沉迷于shell脚本中,这从博客更新就看得出来。

开始是延时播入vpn的脚本,它解决了开机时网络不通的情况下播入vpn的问题。理论上用/etc/network/interfaces配置文件也应可以完成,但我设置了if-up还是有问题,所以就转而考虑shell脚本。

第二个shell脚本是用于中文内码转换的。平时下载得到txt文件大多是gbk编码的,但使用统一的utf8必将更加方便。iconv可以完成这个工作,但它每次只能对一个文件进行处理,显然增加了我的工作量。于是我利用脚本,对通配符进行循环并配合上tellenc对输入文件的内码进行猜测。现在的问题是tellenc的结果不一定很准确,这是只根据前10行进行猜测造成的。可能我会在这方面加入更多硬编码的干预,或者写一个更强大的tellenc来准确的得到编码。

然后就是Google离开中国时写下的一个监控工具。也就是不断的通过域名解析,ping,首页分析,反向dns等方法来观察gfw什么时候,用什么方式对Google下手。这个脚本经过几次强化已经比较完善,稍微修改一下就可以对其他的站点进行同样的监控。

还有一个脚本是用于满足我下列需要:在Apache的.htaccess文件中添加和更新若干Allow From XXX.XXX.XXX.XXX,其中XXX.XXX.XXX.XXX应该是指定的动态域名的ip。在写这个脚本的过程中,我学习到了sed的复杂用法。需要面对的是Apache配置文件的注释风格——只有行首的#被认为是注释的开始。因此我需要在每一个Allow From的上一行加上一个动态域名的md5用于识别,sed将替换掉这个md5的下一行,sed -e “/$md5/N” -e “/$md5/c# $domain : $md5\nAllow From $ip”

还有一个小作品和这个主机分享计划的发起者Michael(bemike.org)有那么一点关系。和他在msn上的聊天中我猜测他有那么一个工具能够截屏并上传到图片分享网站。这很是让人羡慕Mac软件的优秀。于是我写了一个shell脚本来完成同样的事情:scrot用来截取屏幕,yfrog的python api将图片上传,notify-send将url在屏幕上显示,xclip用于将url复制到剪切板。最后,我还用Compiz把PrtScn,Alt+PrtScn都绑定到了这个脚本上。

最终章用于满足我下载的需求——登录到路由器上检查是否有其他电脑接入了网络。然后根据这一点来控制打开和关闭amuled。既不影响上网网速,又最大程度的利用网络,如果路由器能够对流量就进行统计就更好了。

小礼物:bash tips,如果有疑问的话请请先看这个视频

预告:shell脚本暂时告一段落,下面Python脚本和Go语言即将登台演出。

on February 1st, 2010 | No Comments »

Google is leaving… Here’s a shell script to monitor that.

Google正要离开中国。

计划今天晚上去来福士16楼为Google献上一束鲜花。

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Flowers for Google

此外,还特意写了一个bash脚本来记录下这一切。以一个程序员的方法来纪念。

#!/bin/bash
export PATH="/bin:/usr/bin"

LOG=~/google.log
TMP=/tmp/google.index.html
FMT="+%Y-%m-%d %H:%M:%S"
HOST=www.g.cn
NS=202.96.209.5

while [ 1 ]
do
  date "$FMT" | tee -a $LOG
  ips=`dig @$NS $HOST | sed -n -r 's/^.*IN\s+A\s+(.*)$/\1/p'`
  if [ -n "$ips" ]
  then
    for ip in $ips
    do
      wget -O $TMP $ip > /dev/null 2> /dev/null
      google=`cat $TMP | sed -n -r "s/^.*Google.*$/OK/p" \
        | sed -n -r '1p'`
      if [ -n "$google" ]
      then
        echo "$ip OK" | tee -a $LOG
      else
        echo "$ip ERROR" | tee -a $LOG
      fi
    done
  else
    echo "Can't resolve the host: $HOST" | tee -a $LOG
  fi
  sleep 2
done

Updated on 15th Jan 2010:
升级了一下脚本,使得: 1.通过反向解析检测Google是否遭到域名劫持; 2. 支持同时检测多个Google旗下的域名.

#!/bin/bash
export PATH="/bin:/usr/bin"

LOG=~/google.log
TMP=/tmp/google.index.html
FMT="+%Y-%m-%d %H:%M:%S"
GOOGLE_DOMAINS=("www.g.cn" "www.google.cn" "www.google.com")
NS=202.96.209.5

while [ 1 ]
do
  date "$FMT" | tee -a $LOG
  for domain in ${GOOGLE_DOMAINS[@]}
  do
    echo $domain | tee -a $LOG
    ips=`dig @$NS $domain \
      | sed -n -r 's/^.*IN\s+A\s+(.*)$/\1/p'`
    if [ -n "$ips" ]
    then
      for ip in $ips
      do
        server=`nslookup -q=ptr $ip \
          | sed -n -r 's/^.*name = (.*)$/\1/p'`
        is_server_1e100=`echo $server \
          | sed -n -r 's/^.+\.1e100\.net\./OK/p'`
        if [ "$is_server_1e100" = "OK" ]
        then
          wget -O $TMP $ip > /dev/null 2> /dev/null
          is_google=`cat $TMP \
            | sed -n -r "s/^.*Google.*$/OK/p" | sed -n -r '1p'`
          if [ -n "$is_google" ]
          then
              echo "$ip OK" | tee -a $LOG
          else
              echo "$ip ERROR: NOT GOOGLE PAGE" | tee -a $LOG
          fi
        else
          echo "$ip ERROR: DONAME HIJACK" | tee -a $LOG
        fi
      done
    else
      echo "ERROR: STOP RESOLVE: $domain" | tee -a $LOG
    fi
  done
  echo "" | tee -a $LOG
  sleep 2
done

on January 13th, 2010 | No Comments »

Notice : The music in the above article is offered just for trial, and not published under the general license of this blog.

bash script notes (1)

I tied to study the shell script a few times. But on that time, I didn’t get enough knowledge to deal with it. Now, I’m back.

Situation 1

I want to startup the vpn service when the computer startup. I added pon vpn_name to rc.local. However, this can be failed because when this line execute, the network may not be ready. So I decide to write a script to do this. It will wait until the Internet up. Here’s it.

#!/bin/bash

sleep_time=1
export PATH=$PATH:/usr/bin

while [ 1 ]
do
  ping_success=`ping -c 1 -n vpn.yegong.net | \
    sed -n -r 's/^.* (.) received.*$/\1/p'`
  if [ $ping_success -eq 1 ]
  then
    pon yegong
    exit 0
  fi
  sleep $sleep_time
  sleep_time=$(expr $sleep_time + 1)
done
  1. export PATH is very important.
  2. `ping -c 1 -n vpn.yegong.net | sed -n -r ‘s/^.* (.) received.*$/\1/p’`, using ` surround the command to get the output text.
  3. sed -n -r ‘s/^.* (.) received.*$/\1/p’, using sed to simplify the output. It’s the regular expression. Very similar with VIM replace syntax, isn’t it?
  4. if [ $ping_success -eq 1 ], [ condition ] means the test program, equivalent to test condition, so man test will show more usage.
  5. sleep_time=$(expr $sleep_time + 1), it seems that every variable in shell is a string, so you can’t simply write x=x+1. Instead of, expr program read a string expression and output the results. Please notice the space in the expression.

Situation 2

For each text files in a directory, convert the file encoding to utf8.

There’s a simple util can guess the file encoding. It can be found at Wu Yongwei’s Programming Page (Can’t access when I write the article). Then I just need a script to combine the tellenc and iconv.

TMP_FILE="/tmp/utf8lize.output"
ENCODING="utf-8"

if [ $# == 0 ]
then
  echo "Usage: utf8lize FILES"
  exit 1
fi

for f in "$@"
do
  if [ -f "$f" ]
  then
    enc=`tellenc "$f"`
    if [ $enc != $ENCODING ] && [ $enc != "binary" ]
    then
      echo $f : $enc
      cp "$f" "$f.bak"
      iconv -f "$enc" -t "$ENCODING" -o "$TMP_FILE" "$f"
      cp "$TMP_FILE" "$f"
      rm -f "$TMP_FILE"
    fi
  fi
done
  1. $# results the number of arguments when it been executed.
  2. for f in “$@” is the for-each loop. And “$@” indicates the all program arguments. In addition, when you run the program utf8lize *, * will convert to filenames array. Using the ” to surrounding $@ can deal the filename with space.

on December 15th, 2009 | No Comments »

VIM的囧插件vimim

作为一款历史悠久的编辑器,VIM拥有数量众多的脚本插件。山多出杂木,人多出怪物,今天我就遇到一个比较雷人的插件vimim

这名字就够神奇,如果把它写成VimIM的话,就容易理解多了。这正是一个给Vim提供输入法的插件。按作者的说法,输入汉字再也不需要操作系统了,只要能显示,就能在Vim中输入。换作Emacs,倒还是可以考虑一下。现在这种状况,发一封邮件难道还要复制粘贴……确实是很囧

on February 6th, 2009 | 1 Comment »

VIM通用时间戳脚本

曾经写过一个VIM的时间戳脚本,用于自动在源代码文件中添加和更新时间戳,也就是Last Change的注释。前段时间重新写.vimrc,把这个脚本又完善了一下,下载请点

PS. 最近VIM又学到不少技巧。觉得最有用的:!grep可以把visual模式下选中的行用grep工具过滤。其次可以把ii设置成命令模式和插入模式的开关。少了遥远的<Esc>效率高好多,就算不清醒,这个命令也基本上是无害的。

function! TimeStamp(...)
    let sbegin = ''
    let send = ''
    if a:0 >= 1
        let sbegin = a:1.' '
    endif
    if a:0 >= 2
        let send = ' '.a:2
    endif
    let pattern = sbegin . 'Last Change: .\+'
        \. send
    let pattern = '^\s*' . pattern . '\s*$'
    let row = search(pattern, 'n')
    let now = strftime('%Y-%m-%d %H:%M:%S',
        \localtime())
    let now = sbegin . 'Last Change: '
        \. now . send
    if row == 0
        call append(0, now)
    else
        call setline(row, now)
    endif
endfunction

au BufWritePre _vimrc,*.vim   call TimeStamp('"')
au BufWritePre *.c,*.h        call TimeStamp('//')
au BufWritePre *.cpp,*.hpp    call TimeStamp('//')
au BufWritePre *.cxx,*.hxx    call TimeStamp('//')
au BufWritePre *.java         call TimeStamp('//')
au BufWritePre *.rb           call TimeStamp('#')
au BufWritePre *.py           call TimeStamp('#')
au BufWritePre Makefile       call TimeStamp('#')
au BufWritePre *.php
    \call TimeStamp('<?php //', '?>')
au BufWritePre *.html,*htm
    \call TimeStamp('<!--', '-->')

on November 11th, 2008 | 2 Comments »