'
TOKEN_CHAP_PREFIX='''
TOKEN_BODY_END=''
TOKEN_HTML_BEG=''
TOKEN_HTML_END=''
# use extended regex
GREP='egrep'
SED='sed'
# debug only
#html_path='html'
#index_page='index.html'
print_usage()
{
printf "./merge_html.sh [OPTIONS] html_path [index_page]\n"
printf "PARAMETERS:\n"
printf "\thtml_path Path to html files need to be merged.\n"
printf "\tindex_page The html file which contains toc(table of contents).\
If not provided, the html files will be merged in the order of 'ls' output.\n"
printf "OPTIONS:\n"
printf "\t--tok-toc Overide the toc(table of contents) pattern.\n"
printf "\t--tok-prefix Overide the html file tag prefix pattern. \
This will be used to address the html file name.\n"
printf "\t--tok-name Overide the html file name pattern.\n"
printf "\t-h|--help print this help message.\n"
}
# sed script1: remove address token and trailing "
cat>rm_addr_tok.sed
s/\"$//g
}
EOF
# sed script2: replace thumbnail image file name with the regular one
cat>rm_tb_pic.sed
s;${TOKEN_BODY_BEG};;g
s;${TOKEN_BODY_END};;g
s;${TOKEN_HTML_END};;g
}
EOF
# parameters
index_page_default="index.html"
html_path=
index_page=
USE_LS_RESULT=
PARSED_OPT=`getopt -o h --long tok-toc:,tok-prefix:,tok-name:,help\
-n "$0" -- "$@"`
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
eval set -- "$PARSED_OPT"
while true;do
case "$1" in
--tok-toc)
TOKEN_TOC="$2"
echo "TOKEN_TOC: ${TOKEN_TOC}"
shift 2 ;;
--tok-prefix)
TOKEN_CHAP_PREFIX="$2"
echo "TOKEN_CHAP_PREFIX: ${TOKEN_CHAP_PREFIX}"
shift 2 ;;
--tok-name) TOKEN_CHAP_NAME="$2"
echo "TOKEN_CHAP_NAME: ${TOKEN_CHAP_NAME}"
shift 2 ;;
-h|--help) print_usage; shift ; exit 0 ;;
--) shift ; break ;; # delimter of non-option arguments
*) echo " Internal error!" ; exit 1 ;;
esac
done
# now processing non-option arguments...
if [[ -z "$@" ]]; then
read -p "Please specify the folder of html files need to be merged: " html_path
else
for arg do
if [[ -d $arg ]]; then
html_path=$arg
elif [[ -n $html_path && -f "$html_path/$arg" ]]; then
IS_HTML=`egrep -o "^$TOKEN_HTML_BEG|$TOKEN_HTML_END$" "$html_path/$arg"`
if [[ -z "$IS_HTML" ]]; then
echo "Not a html file, try again." ; exit 1
fi
index_page=$arg
fi
done
fi
if [[ -z "$html_path" ]]; then
echo "No html files path specified. Exit."
exit 1
elif [[ -z "$index_page" ]]; then
echo "No index file specified, use 'ls' result instead."
USE_LS_RESULT=1
fi
if [[ -z $USE_LS_RESULT ]]; then
echo "Trying to extract TOC from index page... "
${GREP} -o "${TOKEN_TOC}" "$html_path/$index_page" > toc.tag
if [[ -s toc.tag ]]; then
${GREP} "${TOKEN_TOC}" $html_path/$index_page |${GREP} -o \
"${TOKEN_CHAP_PREFIX}${TOKEN_CHAP_NAME}" | ${SED} -f rm_addr_tok.sed > index.list
else
# fall back to 'ls' result
echo "No table of contents found in index page. Will use file order in $html_path"
# index_page must be excluded from toc ...
ls -t $html_path |${GREP} -iv "$index_page" > index.list
fi
else
# index_page must be excluded from toc ...
ls -t $html_path |${GREP} -iv "$index_page_default" > index.list
fi
echo "TOC: "
cat index.list
echo "Merging html files ... "
for i in $(< index.list)
do
# sed -f rm_tb_pic.sed -i "$html_path/$i"
sed -f rm_html_tag.sed "$html_path/$i" >> ${TARGET}
done
echo "${TOKEN_BODY_END}${TOKEN_HTML_END}" >> ${TARGET}
echo "Merged result: ${TARGET}"
使用说明
以易水老师的《Vi/Vim使用进阶》为例,将html文件下载到本地,例如html目录,然后运行
# ./merge_html.sh html index.html
搞定!其中html是待合并html文件所在目录,index.html是提供目录页的html文档。因为文档里除了index.html外第一章也包含了目录,所以就用grep把index.html从待合并文件列表中去掉了。
如果没有提供包含目录的html文件,或者文件里找不到目录信息,脚本就会使用ls命令生成待合并文件列表,并按这个顺序合并所有html文档。
由于易水老师文章的版权要求,这里就不提供转换好的html文档和pdf文档了,有需要的朋友可以下载脚本自己转一下。
对脚本的几处解释
脚本里的第二个sed命令脚本用来将《Vi/Vim使用进阶》中的小图替换成大图,这样方便直接打印。需要的话请将注释去掉
# sed -f rm_tb_pic.sed -i "$html_path/$i"
脚本使用TOKEN_TOC来匹配目录所在的行,这里有个TODO,就是如果目录是分行写的,匹配就会失败,这个问题留待以后解决。TOKEN_CHAP_PREFIX用来匹配TOC中每个章节的TAG,而TOKEN_CHAP_NAME用来匹配每个章节的标题,这几个TOKEN都可以通过脚本参数手动覆盖。
脚本参考/usr/share/doc/util-linux/examples/getopt-parse.bash脚本,使用了getopt来处理命令行参数。但getopt存在一定的可移植性问题,可以参考stackoverflow上的讨论
getopt的使用
PARSED_OPT=`getopt -o h --long tok-toc:,tok-prefix:,tok-name:,help\
-n "$0" -- "$@"`
在Linux上getopt命令是调用GNU C库的getopt函数实现的,所以支持的选项(options)规则完全一样,例如”-“指定单字符选项,后跟”:“表示该选项必须提供参数(argument),”::“表示参数可选。getopt命令本身也按照UNIX的标准处理自己的参数——需要用”- -“将非选项参数和选项隔离开来。所以需要将调用getopt命令的脚本,即merge_html.sh的命令行全部参数作为一个整体”$@“,并且是非选项参数(Non-opt arguments)传给getopt。并且为了保留命令行参数里的特殊字符需要加”“。
getopt会根据 -o和–long 指定的选项名称和类型对”$@“进行分解,得到的结果保留在PARSED_OPT变量中并将其作为”$@“供后续的while 循环和shift处理。-n ”$0“ 指定merge_html.sh为报错时getopt所使用的身份,因为getopt是给merge_html.sh打工的,所以这样处理后报错的时候显示的就是:”merge_html.sh: invalid option.” 看上去比较符合逻辑。