|
刚写的一个脚本,顺便练练shell编程。
起源:
用 grep 在目录中查找含某个模式的文件,然后拷贝这些匹配的文件到另一个目录下,并保持目录结构(因为做递归查找时,可能子目录中和其它目录中有重名的匹配文件)。
脚本 grepcp.sh 代码:
- #!/bin/sh
- #
- # Description:
- # search file by grep specified "pattern" in "source-path", then copy matched
- # files to directory "target-dir" by name of "target-name".
- #
- # Author: zy
- # Version: 1.0
- #
- # Return:
- # 0: success of copy
- # 1: any kind of failure
- # 2: not failure but not copy
- #
- APPNAME=`basename $0`
- # ++++++++++ usage () ++++++++++
- usage ()
- {
- echo "Usage:"
- echo " $APPNAME [-E -i -R] [-p pattern] [-s source-path] [-t target-dir] [target-name]"
- echo " $APPNAME [-h]"
- echo -e "\nDescription:"
- echo " search file by grep specified "pattern" in "source-path", then copy matched files to directory "target-dir" by name of "target-name"."
- echo -e "\nOptions:"
- echo " [-E -i -R]"
- echo " give options to grep, see grep help."
-
- echo -e "\n [-p pattern]"
- echo " search pattern to grep."
- echo -e "\n [-s source-path]"
- echo " specify source path for grep search, may be an existed directory or regular file."
- echo " if not specified use current working directory as default."
-
- echo -e "\n [-t target-dir]"
- echo " specify target directory to contain "target-name", MUST be an existed directory."
- echo " if not specified use current working directory as default."
- echo -e "\n [target-name]"
- echo " the copy destination name, may be an directory or regular file determined by "source-path"."
- echo " the full destination path is "target-name" with prefix "target-dir"."
- echo " if not specified use the basename of "source-path"."
- echo -e "\nExample:"
- echo " $APPNAME -E -s \\home\\user1\\tc1.txt -p "[0-9]+""
- echo " grep single file "\\home\\user1\\tc1.txt", if it contains pattern "[0-9]+" copy it to current directory."
- echo ""
- echo " $APPNAME -E -s \\home\\user1\\tc1.txt -p "[0-9]+" bak.txt"
- echo " similar as former, but rename file "tc1.txt" to "bak.txt"."
- echo ""
- 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"
- 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."
- echo -e "\nAuthor:\n zy <silon212@gmail.com>"
- }
- # ++++++++++ yes_or_no () ++++++++++
- yes_or_no ()
- {
- VERBOSE=0
- while [ $# -gt 0 ]
- do
- case $1 in
- -v)
- VERBOSE=1
- shift
- ;;
- *)
- _PROMPT=$1
- shift
- ;;
- esac
- done
- INVALID_INPUT=1
- while [ $INVALID_INPUT -ne 0 ]
- do
- echo -n "$_PROMPT (y/n)? "
- read ANS
- INVALID_INPUT=0
- ANS=`echo -n "$ANS" | tr '[A-Z]' '[a-z]'`
- case "$ANS" in
- "y" | "yes")
- return 0
- ;;
- "n" | "no")
- return 1
- ;;
- *)
- if [ $VERBOSE -eq 1 ]; then
- echo "invalid answer: $ANS"
- fi
- INVALID_INPUT=1
- ;;
- esac
- done
- }
- # ++++++++++ main procedure ++++++++++
- # 所有选项
- ALLOPT="$*"
- # echo "ALLOPT is: [$ALLOPT]"
- # 设置默认源路径为当前路径
- SRC_PATH=`pwd`
- # 源路径是否是目录,1 表示目录
- SRC_IS_DIR=1
- # 设置默认目标目录为当前路径
- TARGET_DIR=`pwd`
- # 目标名
- TARGET_NAME=""
- # 实际的目标路径等于 目标目录 + 目标名
- TARGET_PATH=""
- # 传给 grep 的选项
- GREP_OPT=""
- # 传给 grep 的模式
- PATTERN=""
- while getopts :EiRp:s:t:h OPT
- do
- case $OPT in
- E)
- # echo "-E set"
- GREP_OPT="$GREP_OPT -E"
- ;;
- i)
- # echo "-i set"
- GREP_OPT="$GREP_OPT -i"
- ;;
- R)
- # echo "-R set"
- GREP_OPT="$GREP_OPT -R"
- ;;
- p)
- # echo "-p set"
- PATTERN=$OPTARG
- ;;
- s)
- # echo "-s set"
- SRC_PATH=$OPTARG
- ;;
- t)
- # echo "-t set"
- TARGET_DIR=$OPTARG
- ;;
- h)
- # echo "-h set"
- usage;
- exit 2;
- ;;
- *)
- echo -e "$APPNAME: unknown/invalid option: $ALLOPT\n" >&2
- usage;
- exit 1;
- ;;
- esac
- done
- # 让剩余的非选项参数成为拷贝到的目标名
- # 目标名必须在所有选项之后
- shift `expr $OPTIND - 1`
- TARGET_NAME="$@"
- # echo "TARGET_NAME: [$TARGET_NAME]"
- # 目标名不能包含斜线符
- if echo "$TARGET_NAME" | grep "/" >/dev/null 2>&1; then
- echo "$APPNAME: target name MUST NOT contain slash (/), exit now."
- exit 1
- fi
- # echo "pattern: [$PATTERN]"
- # echo "old SRC_PATH: [$SRC_PATH]"
- # echo "old TARGET_DIR: [$TARGET_DIR]"
- # 检查 源路径
- if TEMP=`realpath "$SRC_PATH" 2>/dev/null`; then
- SRC_PATH=$TEMP
- # echo "full SRC_PATH: [$SRC_PATH]"
- if [ -d "$SRC_PATH" ]; then
- SRC_IS_DIR=1
- else
- SRC_IS_DIR=0
- fi
- else
- echo "$APPNAME: failed to get source full path: [$SRC_PATH] (not exist)." >&2
- exit 1;
- fi
- # 检查 目标路径
- if TEMP=`realpath "$TARGET_DIR" 2>/dev/null`; then
- TARGET_DIR=$TEMP
- # echo "full TARGET_DIR: [$TARGET_DIR]"
- if [ ! -d "$TARGET_DIR" ]; then
- echo "$APPNAME: target directory path: [$TARGET_DIR] MUST be a directory." >&2
- exit 1;
- fi
- else
- echo "$APPNAME: failed to get target full directory path: [$TARGET_DIR] (not exist)." >&2
- exit 1;
- fi
- # echo "SRC_PATH: [$SRC_PATH]"
- # echo "TARGET_DIR: [$TARGET_DIR]"
- # 源路径 不能是 目标路径 的前缀
- # 这种情况表示:一个目录向其子目录(或其自身)中进行拷贝,e.g.
- # /aaa/bbb 目录中内容想拷贝到 /aaa/bbb/ccc 目录中
- # 其实只有做递归拷贝时,才会引发问题,而我这里全部都禁止了
- if echo "$TARGET_DIR" | egrep "^$SRC_PATH" >/dev/null 2>&1; then
- 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
- exit 1
- fi
- # echo "GREP_OPT: [$GREP_OPT]"
- # echo "PATTERN: [$PATTERN]"
- # 如果没有指定目标名,则默认用源路径中的名字
- if [ "$TARGET_NAME" = "" ]; then
- TARGET_NAME=`basename "$SRC_PATH"`
- fi
- # 产生拷贝目标全路径
- if [ "$TARGET_DIR" = "/" ]; then
- TARGET_PATH="/$TARGET_NAME"
- else
- TARGET_PATH="$TARGET_DIR/$TARGET_NAME"
- fi
- # echo "TARGET_PATH: [$TARGET_PATH]"
- # 目标路径是否已存在
- if [ -e "$TARGET_PATH" ]; then
- # 禁止向自己进行拷贝
- SRC_REAL=`realpath "$SRC_PATH"`
- TARGET_REAL=`realpath "$TARGET_PATH"`
- if [ "$SRC_REAL" = "$TARGET_REAL" ]; then
- echo "$APPNAME: the target and source MUST NOT be the same path [$TARGET_REAL]."
- exit 1
- fi
- # 提示用户是否删除已有路径
- if yes_or_no -v "target path [$TARGET_PATH] has existed, do you want to remove it"; then
- # echo "u enter yes"
- if ! rm -f -R "$TARGET_PATH" >/dev/null 2>&1; then
- echo "$APPNAME: failed to remove existed target path [$TARGET_PATH]." >&2
- exit 1
- fi
- else
- echo "$APPNAME: you make choice on not removing existed target path [$TARGET_PATH], exit now." >&2
- exit 1
- fi
- fi
- # ++++++++++ 单个文件拷贝 ++++++++++
- if [ $SRC_IS_DIR -eq 0 ]; then
- # 文件含匹配模式
- if grep $GREP_OPT "$PATTERN" "$SRC_PATH" >/dev/null 2>&1; then
- # echo "it match pattern"
- if cp "$SRC_PATH" "$TARGET_PATH"; then
- echo -e "\nfinish copy from [$SRC_PATH] to [$TARGET_PATH]."
- exit 0
- else
- echo -e "\nfailed to copy from [$SRC_PATH] to [$TARGET_PATH]."
- exit 1
- fi
- # 文件不含匹配模式
- else
- echo "source file [$SRC_PATH] not match pattern "$PATTERN"."
- exit 2
- fi
- fi
- # ++++++++++ 目录拷贝 ++++++++++
- MATCH_FILES=`grep $GREP_OPT -l "$PATTERN" "$SRC_PATH"`
- # 源目录中没有一个文件匹配,也可能是没开启 grep 的 -R 选项
- if [ "$MATCH_FILES" = "" ]; then
- echo "source directory [$SRC_PATH] contains no file matching pattern "$PATTERN" in grep option:$GREP_OPT."
- exit 2
- fi
- # 创建目标路径失败
- if ! mkdir "$TARGET_PATH"; then
- echo "$APPNAME: failed to make target path [$TARGET_PATH]." >&2
- exit 1
- fi
- # 进入目标路径(目录)
- OLD_WD=`pwd`
- cd "$TARGET_PATH"
- # i=0
- # 开始循环创建目录,并拷贝匹配的文件
- # 这里用 for 循环不方便,因为路径中可能含有空白字符(空格)
- # for 循环将所有的空白字符(空格 tab LF CR)作为分隔符
- echo "$MATCH_FILES" | while read MATCH_FILE; do
- # echo "$i, full: $MATCH_FILE"
- REL_PATH=`echo "$MATCH_FILE" | awk 'gsub(AWK_SRC_PATH,"") {print $0}' AWK_SRC_PATH="$SRC_PATH/"`
- # echo "$i, relative path: [$REL_PATH]"
- REL_DIR=`dirname "$REL_PATH"`
- # echo "$i, relative dir: [$REL_DIR]"
- # echo "{{{"
- DIR_PARTS=`echo "$REL_DIR" | awk '{gsub("/","\n"); print $0}'`
- echo "$DIR_PARTS" | while read DIR_PART; do
- # echo "DIR_PART: $DIR_PART"
- if [ ! -e "$DIR_PART" ]; then
- mkdir "$DIR_PART"
- fi
- cd "$DIR_PART"
- done
- cd "$TARGET_PATH"
- cp "$MATCH_FILE" "$REL_PATH"
- # echo "}}}"
- # i=`expr $i + 1`
- done
- # 回到原来的工作目录
- cd "$OLD_WD"
- echo -e "\nfinish copy from [$SRC_PATH] to [$TARGET_PATH]."
复制代码
写完后,觉得应该有命令直接可以完成这个功能,比如 cp 的某个选项。如果真的有,请大牛们告诉我,十分感谢。
这个是 grep 配合 cp,同理,还可以写 find 配合 cp。
有什么问题和缺点,请大家说说。
我只是想讨论下更高的方法。 |
|