我最近忙着重构一个历史项目,不过由于客观条件所限,没有测试用例可用,以至于我不得不通过人肉对比新旧服务器的结果集是否一致来判断对错。既然说懒惰是程序员的美德,所以我想还是写一个工具吧,加之结果集为JSON,于是便有了jsondiff.sh。
逻辑很简单,无非就是通过curl在不同的服务器上取得结果集,然后diff即可,不过这里有几点需要注意的地方:首先,JSON就一行,直接diff会失去意义;其次,JSON中汉字会被编码,不利于查看;另外,JSON中字段顺序是无所谓的,所以diff前***排序。说明一下,在格式化JSON数据的时候,我没有用Bash,而是用的PHP:
- #!/bin/bash
- RM=/bin/rm
- PHP=/usr/bin/php
- CURL=/usr/bin/curl
- DIFF=/usr/bin/diff
- VIMDIFF=/usr/bin/vimdiff
- COLORDIFF=/usr/bin/colordiff
- usage() {
- echo "Usage: $0 --uri=<URI> --old=<IP> --new=<IP>"
- }
- format() {
- $PHP -R '
- function ksort_recursive(&$array) {
- if (!is_array($array)) {
- return;
- }
- ksort($array);
- foreach (array_keys($array) as $key) {
- ksort_recursive($array[$key]);
- }
- }
- $options = JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE;
- $array = json_decode($argn, true);
- ksort_recursive($array);
- echo json_encode($array, $options);
- '
- }
- request() {
- $CURL -s -H "Host: $1" "http://$2$3"
- }
- eval set -- $(
- getopt -q -o "h" -l "host:,uri:,old:,new:,vim,help" -- "$@"
- )
- while true; do
- case "$1" in
- --host) HOST=$2; shift 2;;
- --uri) URI=$2; shift 2;;
- --old) OLD=$2; shift 2;;
- --new) NEW=$2; shift 2;;
- --vim) VIM="Y"; shift 1;;
- -h|--help) usage; exit 0;;
- --) break;;
- esac
- done
- if [[ -z "$URI" || -z "$OLD" || -z "$NEW" ]]; then
- usage
- exit 1
- fi
- if [[ -z "$HOST" ]]; then
- HOST="www.foobar.com"
- fi
- OLD_FILE=$(mktemp)
- NEW_FILE=$(mktemp)
- request "$HOST" "$OLD" "$URI" | format > $OLD_FILE
- request "$HOST" "$NEW" "$URI" | format > $NEW_FILE
- if [[ "$VIM" == "Y" ]]; then
- $VIMDIFF $OLD_FILE $NEW_FILE
- elif [[ -x "$COLORDIFF" ]]; then
- $COLORDIFF -u $OLD_FILE $NEW_FILE
- else
- $DIFF -u $OLD_FILE $NEW_FILE
- fi
- $RM -f $OLD_FILE
- $RM -f $NEW_FILE
其中「getopt」的用法值得注意一下,相关参考资料如下:
- Getopt in Bash
- Bash: Preserving Whitespace Using set and eval
虽然我的Shell水平有限,但是我尽可能写得易用:在diff的时候,允许使用多种工具,缺省情况下会优先使用「colordiff」,需要的话还可以激活「vimdiff」。