1

我有一堆(超过 700 个)目录,它们以下列方式命名

1 - *random*name*1*
2 - *random*name*2*
...
725 - *random*name*725*

前导数字 (1-725) 都是目录名称的一部分。

即:所有目录名称都以数字开头,后跟[空格][破折号][空格],然后是一些文本。

我想批量重命名它们,以便它们命名如下:

1 - *random*name*725*
2 - *random*name*724*
...
725 - *random*name*1*

我想“反转”目录名称的数字部分。本质上,我希望“1 -”之后的文本跟随“725 -”,“2 -”之后的文本跟随“724 -”等等。

最简单的方法是什么?我在linux上。

注意: *随机*名称*各不相同,仅用于说明目的。

4

4 回答 4

1

一种使用方式perl。代码已注释。

内容script.pl

#!/usr/bin/env perl

use warnings;
use strict;
use File::Basename qw|basename|;

die qq{Usage: perl $0 <real|trial>\n} unless @ARGV == 1;

my $real = $ARGV[0] eq q|real| ? 1 : 0;

## Filter filenames from the directory that match criteria of OP.
my @files = grep { -d $_ && m/\A(\d+)\s-\s.*\1/ } map { basename $_ } <./*>;

## Get numbers in reverse mode to substitute them later in filenames .
my @nums = map { m/\A(\d+)/; $1 || 0 } reverse @files;

## Process all files, change the number of the file and rename it.
for my $file ( @files ) { 
    (my $new_file = $file) =~ s/\A(\d+)/shift @nums/e;

    if ( $real ) { 
        rename $file, $new_file or warn qq|WARN: Couldn't rename "$file"\n|;
    }   
    else {
        printf qq|%s\n|, qq|rename "$file" "$new_file"|;
    }   
}

列表目录:

ls -1

具有以下输出:

1 - file1file
2 - fi2le
3 - fileeee3
4 - fou4r
5 -  donttouch
6 -  donttouch5either
script.pl

运行脚本:

perl script.pl

再次列出目录:

ls -1

具有以下输出:

1 - fou4r
2 - fileeee3
3 - fi2le
4 - file1file
5 -  donttouch
6 -  donttouch5either
script.pl

编辑:评论中有一个建议让试运行在真正的之前运行,所以我添加了一个论点realtrial. 使用第一个选项,它将进行真正的更改,而使用第二个选项,它将print输出真正的命令,但不运行它们。现在像这样运行脚本:

perl script.pl real

或者

perl script.pl trial
于 2012-07-19T13:38:41.807 回答
1

让我们首先模拟一些文件名:

$ printf 'kjdfh8\nskdjfh9\nckjhv10\naskjdh11\n'
kjdfh8
skdjfh9
ckjhv10
askjdh11

然后通过管道运行它们:

$ printf 'kjdfh8\nskdjfh9\nckjhv10\naskjdh11\n' | sed 's/[^0-9]*\(.*\)/\1 &/' | sort -nr | cut -d\  -f2-
askjdh11
ckjhv10
skdjfh9
kjdfh8

这是如何运作的?其实很简单。首先,我们使用sed将任何数字内容的副本放在行首。接下来,我们进行排序。最后,我们使用cut去掉行首的数字。瞧!

现在......这只是颠倒了顺序。这是您问题中的问题之一,但您的最终目标是重命名事物。我们可以用另一个也是 while 循环的管道来扩展上面的列表。这次我将把内容分开以便于阅读。

#!/bin/bash

n=0
printf 'kjdfh8\nskdjfh9\nckjhv10\naskjdh11\n' \
| sed 's/[^0-9]*\(.*\)/\1 &/' | sort -nr | cut -d\  -f2- \
| while read file; do
    base="${file/[0-9]*/}"
    ((n++))
    printf 'mv "%s" "%s%s"\n' "$file" "$base" "$n"
  done

这个 while 循环读取我们在上面创建的文件列表。它通过删除文件名中以第一个数字开头的所有内容来获得“基础”。它增加一个计数器(用于新名称),然后打印一个mv带有旧文件名、基数和计数器的命令。

同样,此脚本的输出是一组mv命令,您可以通过 shell 进行管道传输。

显然,要在您的文件上使用它,您需要将初始printf命令替换为生成文件列表的内容。

注意:通常不建议以这种方式处理文件名,因为文件名中出现意外或特殊字符(甚至空格)并打乱您的计划的风险。仅当您确信自己理解它时才这样做。

于 2012-07-19T13:58:13.360 回答
0

如果这个 awk 解决方案适合你吗?

命令:

ls -F|grep "/$"|sort|awk 'BEGIN{FS=" - ";l=5;}{o=$0;gsub("/$","",o);gsub($1"/$",l--);if(o!=$0)printf "mv \"%s\" \"%s\"\n",o,$0 }' |sh

这里的 5 是硬编码的,在你的情况下是 725。:) 当然它可以由 wc -l 设置,但我有点懒惰。

测试:

kent$  tree -d
.
|-- 1 - foo1
|-- 2 - foo2
|-- 3 - foo3
|-- 4 - foo4
`-- 5 - foo5

5 directories

#this will only print the generated mv command:

kent$  ls -F|grep "/$"|sort|awk 'BEGIN{FS=" - ";l=5;}{o=$0;gsub("/$","",o);gsub($1"/$",l--);if(o!=$0)printf "mv \"%s\" \"%s\"\n",o,$0 }'   
mv "1 - foo1" "1 - foo5"
mv "2 - foo2" "2 - foo4"
mv "4 - foo4" "4 - foo2"
mv "5 - foo5" "5 - foo1"

# if you pipe the generated command to "sh", they will be executed:

kent$  ls -F|grep "/$"|sort|awk 'BEGIN{FS=" - ";l=5;}{o=$0;gsub("/$","",o);gsub($1"/$",l--);if(o!=$0)printf "mv \"%s\" \"%s\"\n",o,$0 }' |sh


#result:

kent$  tree -d
.
|-- 1 - foo5
|-- 2 - foo4
|-- 3 - foo3
|-- 4 - foo2
`-- 5 - foo1

5 directories
于 2012-07-19T13:53:44.043 回答
0

这个:

find /path/to/the/directories/location/ -depth -exec mv '{}' $(basename '{}')$(echo $(date "+%H%M%S%N")) \;
于 2013-07-02T09:06:19.200 回答