LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 685|回复: 2

【分享】刚写的一个搜索/备份文件的脚本

[复制链接]
发表于 2010-5-6 14:24:32 | 显示全部楼层 |阅读模式
刚写的一个脚本,顺便练练shell编程。

起源:

用 grep 在目录中查找含某个模式的文件,然后拷贝这些匹配的文件到另一个目录下,并保持目录结构(因为做递归查找时,可能子目录中和其它目录中有重名的匹配文件)。

脚本 grepcp.sh 代码:
  1. #!/bin/sh
  2. #
  3. # Description:
  4. #  search file by grep specified "pattern" in "source-path", then copy matched
  5. #  files to directory "target-dir" by name of "target-name".
  6. #
  7. # Author: zy
  8. # Version: 1.0
  9. #
  10. # Return:
  11. #   0: success of copy
  12. #   1: any kind of failure
  13. #   2: not failure but not copy
  14. #
  15. APPNAME=`basename $0`
  16. # ++++++++++ usage () ++++++++++
  17. usage ()
  18. {
  19.         echo "Usage:"
  20.         echo "  $APPNAME [-E -i -R] [-p pattern] [-s source-path] [-t target-dir] [target-name]"
  21.         echo "  $APPNAME [-h]"
  22.         echo -e "\nDescription:"
  23.         echo "  search file by grep specified "pattern" in "source-path", then copy matched files to directory "target-dir" by name of "target-name"."
  24.         echo -e "\nOptions:"
  25.         echo "  [-E -i -R]"
  26.         echo "  give options to grep, see grep help."
  27.        
  28.         echo -e "\n  [-p pattern]"
  29.         echo "  search pattern to grep."
  30.         echo -e "\n  [-s source-path]"
  31.         echo "  specify source path for grep search, may be an existed directory or regular file."
  32.         echo "  if not specified use current working directory as default."
  33.        
  34.         echo -e "\n  [-t target-dir]"
  35.         echo "  specify target directory to contain "target-name", MUST be an existed directory."
  36.         echo "  if not specified use current working directory as default."
  37.         echo -e "\n  [target-name]"
  38.         echo "  the copy destination name, may be an directory or regular file determined by "source-path"."
  39.         echo "  the full destination path is "target-name" with prefix "target-dir"."
  40.         echo "  if not specified use the basename of "source-path"."
  41.         echo -e "\nExample:"
  42.         echo "  $APPNAME -E -s \\home\\user1\\tc1.txt -p "[0-9]+""
  43.         echo "  grep single file "\\home\\user1\\tc1.txt", if it contains pattern "[0-9]+" copy it to current directory."
  44.         echo ""
  45.         echo "  $APPNAME -E -s \\home\\user1\\tc1.txt -p "[0-9]+" bak.txt"
  46.         echo "  similar as former, but rename file "tc1.txt" to "bak.txt"."
  47.         echo ""
  48.         echo "  $APPNAME -E -i -R -t .. -p "[a-z]([a-z0-9]*[-_]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[\\.][a-z]{2,3}([\\.][a-z]{2})?" bak"
  49.         echo "  search current directory recursively by grep pattern "[a-z]([a-z0-9]*[-_]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[\\.][a-z]{2,3}([\\.][a-z]{2})?" (take for E-mail), then copy matched files to "../bak", and keep their directory hierarchy."
  50.         echo -e "\nAuthor:\n  zy <silon212@gmail.com>"
  51. }
  52. # ++++++++++ yes_or_no () ++++++++++
  53. yes_or_no ()
  54. {
  55.         VERBOSE=0
  56.         while [ $# -gt 0 ]
  57.         do
  58.                 case $1 in
  59.                 -v)
  60.                         VERBOSE=1
  61.                         shift
  62.                 ;;
  63.                 *)
  64.                         _PROMPT=$1
  65.                         shift
  66.                 ;;
  67.                 esac
  68.         done
  69.         INVALID_INPUT=1
  70.         while [ $INVALID_INPUT -ne 0 ]
  71.         do
  72.                 echo -n "$_PROMPT (y/n)? "
  73.                 read ANS
  74.                 INVALID_INPUT=0
  75.                 ANS=`echo -n "$ANS" | tr '[A-Z]' '[a-z]'`
  76.                 case "$ANS" in
  77.                 "y" | "yes")
  78.                         return 0
  79.                         ;;
  80.                 "n" | "no")
  81.                         return 1
  82.                         ;;
  83.                 *)
  84.                         if [ $VERBOSE -eq 1 ]; then
  85.                                 echo "invalid answer: $ANS"
  86.                         fi
  87.                         INVALID_INPUT=1
  88.                         ;;
  89.                 esac
  90.         done
  91. }
  92. # ++++++++++ main procedure ++++++++++
  93. # 所有选项
  94. ALLOPT="$*"
  95. # echo "ALLOPT is: [$ALLOPT]"
  96. # 设置默认源路径为当前路径
  97. SRC_PATH=`pwd`
  98. # 源路径是否是目录,1 表示目录
  99. SRC_IS_DIR=1
  100. # 设置默认目标目录为当前路径
  101. TARGET_DIR=`pwd`
  102. # 目标名
  103. TARGET_NAME=""
  104. # 实际的目标路径等于 目标目录 + 目标名
  105. TARGET_PATH=""
  106. # 传给 grep 的选项
  107. GREP_OPT=""
  108. # 传给 grep 的模式
  109. PATTERN=""
  110. while getopts :EiRp:s:t:h OPT
  111. do
  112.         case $OPT in
  113.         E)
  114.                 # echo "-E set"
  115.                 GREP_OPT="$GREP_OPT -E"
  116.                 ;;
  117.         i)
  118.                 # echo "-i set"
  119.                 GREP_OPT="$GREP_OPT -i"
  120.                 ;;
  121.         R)
  122.                 # echo "-R set"
  123.                 GREP_OPT="$GREP_OPT -R"
  124.                 ;;
  125.         p)
  126.                 # echo "-p set"
  127.                 PATTERN=$OPTARG
  128.                 ;;
  129.         s)
  130.                 # echo "-s set"
  131.                 SRC_PATH=$OPTARG
  132.                 ;;
  133.         t)
  134.                 # echo "-t set"
  135.                 TARGET_DIR=$OPTARG
  136.                 ;;
  137.         h)
  138.                 # echo "-h set"
  139.                 usage;
  140.                 exit 2;
  141.                 ;;
  142.         *)
  143.                 echo -e "$APPNAME: unknown/invalid option: $ALLOPT\n" >&2
  144.                 usage;
  145.                 exit 1;
  146.                 ;;
  147.         esac
  148. done
  149. # 让剩余的非选项参数成为拷贝到的目标名
  150. # 目标名必须在所有选项之后
  151. shift `expr $OPTIND - 1`
  152. TARGET_NAME="$@"
  153. # echo "TARGET_NAME: [$TARGET_NAME]"
  154. # 目标名不能包含斜线符
  155. if echo "$TARGET_NAME" | grep "/" >/dev/null 2>&1; then
  156.         echo "$APPNAME: target name MUST NOT contain slash (/), exit now."
  157.         exit 1
  158. fi
  159. # echo "pattern: [$PATTERN]"
  160. # echo "old SRC_PATH: [$SRC_PATH]"
  161. # echo "old TARGET_DIR: [$TARGET_DIR]"
  162. # 检查 源路径
  163. if TEMP=`realpath "$SRC_PATH" 2>/dev/null`; then
  164.         SRC_PATH=$TEMP
  165.         # echo "full SRC_PATH: [$SRC_PATH]"
  166.         if [ -d "$SRC_PATH" ]; then
  167.                 SRC_IS_DIR=1
  168.         else
  169.                 SRC_IS_DIR=0
  170.         fi
  171. else
  172.         echo "$APPNAME: failed to get source full path: [$SRC_PATH] (not exist)." >&2
  173.         exit 1;
  174. fi
  175. # 检查 目标路径
  176. if TEMP=`realpath "$TARGET_DIR" 2>/dev/null`; then
  177.         TARGET_DIR=$TEMP
  178.         # echo "full TARGET_DIR: [$TARGET_DIR]"
  179.         if [ ! -d "$TARGET_DIR" ]; then
  180.                 echo "$APPNAME: target directory path: [$TARGET_DIR] MUST be a directory." >&2
  181.                 exit 1;
  182.         fi
  183. else
  184.         echo "$APPNAME: failed to get target full directory path: [$TARGET_DIR] (not exist)." >&2
  185.         exit 1;
  186. fi
  187. # echo "SRC_PATH: [$SRC_PATH]"
  188. # echo "TARGET_DIR: [$TARGET_DIR]"
  189. # 源路径 不能是 目标路径 的前缀
  190. # 这种情况表示:一个目录向其子目录(或其自身)中进行拷贝,e.g.
  191. # /aaa/bbb 目录中内容想拷贝到 /aaa/bbb/ccc 目录中
  192. # 其实只有做递归拷贝时,才会引发问题,而我这里全部都禁止了
  193. if echo "$TARGET_DIR" | egrep "^$SRC_PATH" >/dev/null 2>&1; then
  194.         echo "$APPNAME: source path [$SRC_PATH] MUST NOT be prefix of target directory [$TARGET_DIR], it will bring weird operation when copy directory contents into it's sub-directory recursively." >&2
  195.         exit 1
  196. fi
  197. # echo "GREP_OPT: [$GREP_OPT]"
  198. # echo "PATTERN: [$PATTERN]"
  199. # 如果没有指定目标名,则默认用源路径中的名字
  200. if [ "$TARGET_NAME" = "" ]; then
  201.         TARGET_NAME=`basename "$SRC_PATH"`
  202. fi
  203. # 产生拷贝目标全路径
  204. if [ "$TARGET_DIR" = "/" ]; then
  205.         TARGET_PATH="/$TARGET_NAME"
  206. else
  207.         TARGET_PATH="$TARGET_DIR/$TARGET_NAME"
  208. fi
  209. # echo "TARGET_PATH: [$TARGET_PATH]"
  210. # 目标路径是否已存在
  211. if [ -e "$TARGET_PATH" ]; then
  212.         # 禁止向自己进行拷贝
  213.         SRC_REAL=`realpath "$SRC_PATH"`
  214.         TARGET_REAL=`realpath "$TARGET_PATH"`
  215.         if [ "$SRC_REAL" = "$TARGET_REAL" ]; then
  216.                 echo "$APPNAME: the target and source MUST NOT be the same path [$TARGET_REAL]."
  217.                 exit 1
  218.         fi
  219.         # 提示用户是否删除已有路径
  220.         if yes_or_no -v "target path [$TARGET_PATH] has existed, do you want to remove it"; then
  221.                 # echo "u enter yes"
  222.                 if ! rm -f -R "$TARGET_PATH" >/dev/null 2>&1; then
  223.                         echo "$APPNAME: failed to remove existed target path [$TARGET_PATH]." >&2
  224.                         exit 1
  225.                 fi
  226.         else
  227.                 echo "$APPNAME: you make choice on not removing existed target path [$TARGET_PATH], exit now." >&2
  228.                 exit 1
  229.         fi
  230. fi
  231. # ++++++++++ 单个文件拷贝 ++++++++++
  232. if [ $SRC_IS_DIR -eq 0 ]; then
  233.         # 文件含匹配模式
  234.         if grep $GREP_OPT "$PATTERN" "$SRC_PATH" >/dev/null 2>&1; then
  235.                 # echo "it match pattern"
  236.                 if cp "$SRC_PATH" "$TARGET_PATH"; then
  237.                         echo -e "\nfinish copy from [$SRC_PATH] to [$TARGET_PATH]."
  238.                         exit 0
  239.                 else
  240.                         echo -e "\nfailed to copy from [$SRC_PATH] to [$TARGET_PATH]."
  241.                         exit 1
  242.                 fi
  243.         # 文件不含匹配模式
  244.         else
  245.                 echo "source file [$SRC_PATH] not match pattern "$PATTERN"."
  246.                 exit 2
  247.         fi
  248. fi
  249. # ++++++++++ 目录拷贝 ++++++++++
  250. MATCH_FILES=`grep $GREP_OPT -l "$PATTERN" "$SRC_PATH"`
  251. # 源目录中没有一个文件匹配,也可能是没开启 grep 的 -R 选项
  252. if [ "$MATCH_FILES" = "" ]; then
  253.         echo "source directory [$SRC_PATH] contains no file matching pattern "$PATTERN" in grep option:$GREP_OPT."
  254.         exit 2
  255. fi
  256. # 创建目标路径失败
  257. if ! mkdir "$TARGET_PATH"; then
  258.         echo "$APPNAME: failed to make target path [$TARGET_PATH]." >&2
  259.         exit 1
  260. fi
  261. # 进入目标路径(目录)
  262. OLD_WD=`pwd`
  263. cd "$TARGET_PATH"
  264. # i=0
  265. # 开始循环创建目录,并拷贝匹配的文件
  266. # 这里用 for 循环不方便,因为路径中可能含有空白字符(空格)
  267. # for 循环将所有的空白字符(空格 tab LF CR)作为分隔符
  268. echo "$MATCH_FILES" | while read MATCH_FILE; do
  269.         # echo "$i, full: $MATCH_FILE"
  270.         REL_PATH=`echo "$MATCH_FILE" | awk 'gsub(AWK_SRC_PATH,"") {print $0}' AWK_SRC_PATH="$SRC_PATH/"`
  271.         # echo "$i, relative path: [$REL_PATH]"
  272.         REL_DIR=`dirname "$REL_PATH"`
  273.         # echo "$i, relative dir: [$REL_DIR]"
  274.         # echo "{{{"
  275.         DIR_PARTS=`echo "$REL_DIR" | awk '{gsub("/","\n"); print $0}'`
  276.         echo "$DIR_PARTS" | while read DIR_PART; do
  277.                 # echo "DIR_PART: $DIR_PART"
  278.                 if [ ! -e "$DIR_PART" ]; then
  279.                         mkdir "$DIR_PART"
  280.                 fi
  281.                 cd "$DIR_PART"
  282.         done
  283.         cd "$TARGET_PATH"
  284.         cp "$MATCH_FILE" "$REL_PATH"
  285.         # echo "}}}"
  286.         # i=`expr $i + 1`
  287. done
  288. # 回到原来的工作目录
  289. cd "$OLD_WD"
  290. echo -e "\nfinish copy from [$SRC_PATH] to [$TARGET_PATH]."
复制代码

写完后,觉得应该有命令直接可以完成这个功能,比如 cp 的某个选项。如果真的有,请大牛们告诉我,十分感谢。

这个是 grep 配合 cp,同理,还可以写 find 配合 cp。

有什么问题和缺点,请大家说说。

我只是想讨论下更高的方法。
发表于 2010-5-7 13:54:58 | 显示全部楼层
无聊的人没cu多吧
回复 支持 反对

使用道具 举报

 楼主| 发表于 2010-5-7 17:24:03 | 显示全部楼层
你难道是?cu,唉!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表