免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1760 | 回复: 2

【分享】刚写的一个搜索/备份文件的脚本 [复制链接]

论坛徽章:
0
发表于 2010-05-06 12:07 |显示全部楼层
刚写的一个脚本,顺便练练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 <silon212@gmail.com>
  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。

有什么问题和缺点,请大家说说。优化帝请留言。我的感觉是很笨。

论坛徽章:
0
发表于 2010-05-06 12:09 |显示全部楼层
支持一下

论坛徽章:
0
发表于 2010-05-06 12:10 |显示全部楼层
本帖最后由 silon212 于 2010-05-06 12:13 编辑

一楼在手。

重复:

如果有好的方法能达到 grep + cp,或 find + cp,一定告诉我哦,最好是一行式命令。

LS的,你发的太快了吧。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP