自动重新创建许多不同的链接以重命名目录或文件

时间:2013-03-15 11:54:14

标签: linux perl shell solaris ksh

我需要在Linux / Solaris机器中重命名很多目录和文件。 有许多链接指向那些目录/文件。

首先,我创建以下脚本,以便找到我想要重命名的所有目录(例如DOMAIN.city.countryDOMAIN.type.country等。) 和他们的联系。

该脚本从文件info.file文件中获取节点的旧名称和新名称。每行中的第一个字段表示旧目录名称,第二个字段表示新目录名称。

档案find_and_recreate_link_to_new_dir.ksh

while read -r line ; do 

[[ -z $line ]] && continue

name_old_dir=` echo $line | awk '{print $1}' `
name_new_dir=` echo $line | awk '{print $2}' `

find / -type l -exec ls -l 2>/dev/null '{}' \; |  perl -ne'BEGIN { $str = shift(@ARGV); }     print if /\Q$str\E/; ' $name_old_dir

done  < /tmp/test/info.file

档案/tmp/test/info.file

    DOMAIN.city.country DOMAIN.world.country
    DOMAIN.city1.country DOMAIN.world1.country
    ...

当我运行我的脚本时,我得到了这个输出

lrwxrwxrwx 1 root root 19 Mar 15 11:22 /var/test/info.domain2.com -> DOMAIN.city.country
lrwxrwxrwx 1 root root 19 Mar 15 11:22 /var/test/info.domain.com -> DOMAIN.city.country
lrwxrwxrwx 1 root root 19 Mar 15 11:22 /var/test/info.domain1.com -> DOMAIN.city.country
lrwxrwxrwx 1 root root 5 Mar 15 11:57 /var/test/DOMAIN.type.country -> mkdir
lrwxrwxrwx 1 root root 19 Mar 15 11:58 /var/test/info.tyep.com -> DOMAIN.type.country
...

现在我需要在此脚本中添加一个部分,该部分将重新创建指向新目录的当前链接。例如

/var/test/info.domain2.com -> DOMAIN.world.country

请告知最佳解决方案,以自动重新创建指向新目录的当前链接。

2 个答案:

答案 0 :(得分:1)

如果我正确理解了问题,您希望根据来自find_and_recreate_link_to_new_dir.ksh的信息创建符号链接。

所以,如果你得到

lrwxrwxrwx 1 root root 19 Mar 15 11:22 /var/test/info.domain2.com -> DOMAIN.city.country

您需要创建链接

/var/test/info.domain2.com -> DOMAIN.city.country

在这种情况下,我建议您存储这两个参数并使用它们调用ln -s。将数据保存在文件中会很有意思,让我们说FILE_WITH_NEW_DATA:

find / -type l -exec ls -l 2>/dev/null '{}' \; |  perl -ne'BEGIN { $str = shift(@ARGV); }     print if /\Q$str\E/; ' $name_old_dir >> FILE_WITH_NEW_DATA

然后,给出字符串

lrwxrwxrwx 1 root root 19 Mar 15 11:22 /var/test/info.domain2.com -> DOMAIN.city.country

您可以通过以下方式获取参数:

link = $(awk 'N=NF-2 {print $N}')
file = $(awk '{print $NF}')

然后使用ln -s $file $link创建符号链接。

一起,

... 

find / -type l -exec ls -l 2>/dev/null '{}' \; |  perl -ne'BEGIN { $str = shift(@ARGV); }     print if /\Q$str\E/; ' $name_old_dir >> FILE_WITH_DATA

...


while read -r line ; do 

  [[ -z $line ]] && continue

  link = $(echo $line | awk 'N=NF-2 {print $N}')
  file = $(echo $line | awk '{print $NF}')

  # begin OPTION 1) we delete previous link
  rm $link
  ln -s $file $link
  # end OPTION 1)

  # begin OPTION 2) we force the link to be created and replace the older one
  ln -s -f $file $link
  # end OPTION 2)

done  < FILE_WITH_NEW_DATA

答案 1 :(得分:0)

您的脚本效率不高。对于要映射的每个文件,它运行awk两次(与后面的内容相比,这是次要的peccadillo),然后在整个机器上运行find寻找符号链接,并运行ls对于每个符号链接,仅选择显示旧名称的链接。

遍历整个文件系统的速度极慢;你只能负担一次。

我认为您应该使用更复杂的Perl脚本(因为您已经在使用Perl)来完成大部分工作,但您可以保留find命令,尽管也可以使用Perl的文件::查找(核心)模块。

Perl脚本将被赋予映射文件(/tmp/test/info.file)作为参数,以及来自find命令的符号链接:

  • 阅读文件中的行
  • 相应地重命名文件
  • 同时在哈希中记录旧名称和新名称以进行映射

  • 阅读符号链接名称的标准输入

  • 使用readlink读取符号链接的目标
  • 如果链接与任何旧名称匹配
    • 删除旧链接
    • 创建新链接

shell脚本变为:

find / -type l -print | perl rebuild.links.pl /tmp/test/info.file

Perl脚本变为(警告 - 未经测试的代码):

#!/usr/bin/env perl
use strict;
use warnings;

my %map;

die "Usage: $0 map-file\n" unless @ARGV == 1;

{
    open my $fh, '<', $ARGV[0] or die "Unable to open $ARGV[0] for reading\n";
    while (<$fh>)
    {
        chomp;
        next if m/^\s*$/;  # Skip blank lines
        next if m/^#/;     # Skip comment lines
        my($old, $new) = split /\s+/, $_;
        if (-f $old)
        {
            $map{$old} = $new;
            rename $old, $new or die "Failed to rename $old to $new\n";
        }
    }
    close $fh;
}

shift @ARGV;  # Lose the file name

# Read standard input for symlink names
while (my $source = <>)
{
     chomp $source;
     my $oldlink = readlink $_;
     MAP:
     for my $old (keys %map)
     {
         if ($oldlink =~ m%$old%)
         {
             my $target = $oldlink;
             $target =~ s%$old%$map{$old}%;
             die "Mapped name $target does not exist for symlink $source (old link $oldlink)\n"
                 unless -e $target;
             unlink $source or die "Failed to remove symlink $source\n";
             symlink $target, $source or die "Failed to create symlink $source for $target\n";
             last MAP;
         }
     }
}

显示的代码编译(perl -c)但尚未运行。将其视为我认为您的代码应该是什么样子的改进大纲。在您考虑测试之前应该确保理解的问题包括:

  • 此脚本处理时,/tmp/test/info.file中的名称是否正确?它们是绝对名称,还是相对于当前目录的简单名称?
  • 显示的映射是否正常工作?

为了调试代码,我可能会:

  • 在顶部添加use constant debug => 1;
  • 使用条件unlink打印而不是执行symlinkrenameif debug;等。
  • 在子目录而不是find上运行/命令,直到我确定我知道发生了什么为止。

小心 - 这是危险的东西。