为什么bash中的mv命令会删除文件?

时间:2013-08-14 14:53:26

标签: bash

运行以下脚本来重命名当前文件夹中的所有.jpg文件有时效果很好,但它经常删除它重命名的一个或多个文件。如何在不删除文件的情况下编写脚本来重命名文件?这是在Mac OSX 10.8上使用GNU bash,版本3.2.48

运行的

这是一个示例文件列表,我将更改为插图:

原始文件

red.jpg
blue.jpg
green.jpg
如果counter设置为5

,则

重命名文件

file_5.jpg
file_6.jpg
file_7.jpg

相反,我通常会丢失一个或多个文件

#!/bin/bash  

counter=5

for file in *.jpg; do

    echo renaming "$file" to "file_${counter}.jpg";
    mv "$file" "file_${counter}.jpg";
    let "counter+=1";

done

** 更新 **

它似乎不再是删除文件,但输出仍然不是预期的。例如:

file_3.jpg
file_4.jpg

变成

file_3.jpg
file_5.jpg

当计数器设置为4时,预期输出为

file_4.jpg
file_5.jpg

-

#!/bin/bash  

counter=3

for file in *.jpg; do

    if [[ -e file_${counter}.jpg ]] ; then
        echo Skipping "$file", file exists.
    else
        echo renaming "$file" to "file_${counter}.jpg"
        mv "$file" "file_${counter}.jpg"
    fi

    let "counter+=1"

done

5 个答案:

答案 0 :(得分:10)

问题是某些文件已经具有与目标名称对应的名称。例如,如果有文件

file_1.jpg
file_7.jpg

,您从counter=7开始,在第一步中使用file_7.jpg覆盖file_1.jpg,然后将其重命名为file_8.jpg

您可以使用mv -n来阻止clobbering(如果支持),或者在运行命令之前测试是否存在

if [[ -e file_${counter}.jpg ]] ; then
    echo Skipping "$file", file exists.
else
    mv "$file" "file_${counter}.jpg"
fi

答案 1 :(得分:2)

我认为你正在使用glob来解决一个明显的问题。如果glob匹配file_2.jpg,它将尝试创建file_file_2.jpg(我不是说在字面意义上,只是你将重新处理你已经处理过的文件)。要解决此问题,您需要确保初始glob表达式与您已移动的文件不匹配:

shopt -s extglob

i=0
for f in !(file_*).jpg ; do
    while [[ -e "file_${i}.jpg" ]] ; do
        (( i++ ))
    done

    mv -v "$f" "file_$i.jpg"
    (( i++ ))
done

答案 2 :(得分:1)

什么choroba说是正确的。您也可以使用:

mv "$file" "file_${counter}.jpg" -n

在目标文件名已存在时忽略移动,或

mv "$file" "file_${counter}.jpg" -i

询问是否应该覆盖。

答案 3 :(得分:1)

不应迭代*.jpg,而应跳过已重命名的文件,即file_[0-9]*.jpg,然后像这样运行循环:

counter=5

while read file; do
   echo renaming "$file" to "file_${counter}.jpg";
   mv -n "$file" "file_${counter}.jpg";
   let "counter+=1";
done < <(find . -maxdepth 1 -name "*.jpg" -not -name "file_[0-9]*.jpg")

答案 4 :(得分:1)

另一种方法是继续计数直到文件不存在:

#!/bin/bash  

counter=1
shopt -s extglob

for file in *.jpg; do
    [[ $file == ?(*/)file_+([[:digit:]]).jpg ]] && continue

    until
        newname=file_$(( counter++ )).jpg
        [[ ! -e $newname ]]
    do
        continue
    done

    echo "renaming $file to $newname.";

    mv -i "$file" "$newname" ## Remove the -i option if you think it's safe already.
done

递归时做事:

#!/bin/bash  

shopt -s  extglob    
counter=1

while read file; do
    dirprefix=${file%%+([^/])

    until
        newfile=$dirprefix/file_$(( counter++ )).jpg
        [[ ! -e $newfile ]]
    do
        continue
    done

    echo "renaming $file to $newfile."

    mv -i "$file" "$newfile" ## Remove the -i option if you think it's safe already.
done < <(find -type f -name '*.jpg' -and -not -regex '^.*/file_[0-9]\+$')