递归Shell脚本和文件扩展名问题

时间:2013-06-30 14:08:57

标签: bash shell

我的脚本有问题。该脚本应该通过所有文件和所有子目录和子文件(递归)。如果文件以扩展名.txt结尾,我需要用新的字符/单词替换文本中的字符/单词,然后将其复制到现有目录中。第一个参数是我需要开始搜索的目录,第二个是旧的char / word,第三个是新的char / word,第四个是要复制文件的目录。该脚本通过文件进行,但只进行替换并从原始目录复制文件。这是脚本

#!/bin/bash

funk(){
  for file in `ls $1`
  do
    if [ -f $file ]
    then
      ext=${file##*.}
      if [ "$ext" = "txt" ]
      then
        sed -i "s/$2/$3/g" $file
        cp $file $4
      fi
    elif [ -d $file ]
    then
      funk $file $2 $3 $4
    fi
  done
}

if [ $# -lt 4 ]
then
  echo "Need more arg"
  exit 2;
fi

cw=$1
a=$2
b=$3
od=$4
funk $cw $a $b $od

2 个答案:

答案 0 :(得分:6)

你在这里使用了很多不好的做法:缺少报价,你正在解析ls的输出......只要文件名包含其他有趣符号的空格,这一切就会中断。

如果您使用globstar可选行为或find,则无需递归。

这是前者的可能性,希望能够向您展示更好的实践:

#!/bin/bash

shopt -s globstar
shopt -s nullglob

funk() {
    local search=${2//\//\\/}
    local replace=${3//\//\\/}

    for f in "$1"/**.txt; do
        sed -i "s/$search/$replace/g" -- "$f"
        cp -nvt "$4" -- "$f"
    done
}

if (($#!=4)); then
    echo >&2 "Need 4 arguments"
    exit 1
fi

funk "$@"

使用find

的功能相同的功能
#!/bin/bash

funk() {
    local search=${2//\//\\/}
    local replace=${3//\//\\/}

    find "$1" -name '*.txt' -type f -exec sed -i "s/$search/$replace/g" -- {} \; -exec cp -nvt "$4" -- {} \;
}

if (($#!=4)); then
    echo >&2 "Need 4 arguments"
    exit 1
fi

funk "$@"

cp我正在使用

  • -n开关:没有clobber,以便不覆盖现有文件。如果您的mv版本支持它,请使用它,除非您确实要覆盖文件。
  • -v开关:详细,将显示已移动的文件(可选)。
  • -t开关:-t后跟一个目录告诉复制到此目录。以这种方式使用cp是一件非常好的事情:想象而不是给出现有的目录,你给出一个现有的文件:没有这个功能,这个文件将会被多次覆盖(好吧,如果省略-n选项就会出现这种情况)!使用此功能,现有文件将保持安全。

另请注意--的使用。如果您的cpsed支持它(GNU sedcp就属于这种情况),请始终使用它!它意味着现在选项的结束。如果您不使用它,并且文件名以连字符开头,则会混淆尝试解释选项的命令。使用此--,我们可以放置一个可能以连字符开头的文件名。

请注意,在搜索替换模式中,我将所有斜杠/替换为其转义格式\/,以免与其冲突如果斜线恰好出现在搜索替换中,则sed中的分隔符。

享受!

答案 1 :(得分:-2)

正如所指出的,循环查找输出不是一个好主意。它也不支持搜索和替换中的斜杠。

检查gniourf_gniourf's answer


如何使用find呢?

 #!/bin/bash

 funk () {
     local dir=$1; shift
     local search=$1; shift
     local replace=$1; shift
     local dest=$1; shift

     mkdir -p "$dest"
     for file in `find $dir -name *.txt`; do
         sed -i "s/$search/$replace/g" "$file"
         cp "$file" "$dest"
     done
 }

 if [[ $# -lt 4 ]] ; then
     echo "Need 4 arguments"
     exit 2;
 fi

 funk "$@"

虽然您可能在子目录中具有相同名称的文件,但这些文件将被覆盖。这是你的问题吗?