1

我需要重命名我的 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

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

4

2 回答 2

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
于 2013-03-15T12:38:09.030 回答
0

您的脚本效率不高。对于每个要映射的文件,它运行awk两次(与下面的相比,这是一个小错误),然后find在整个机器上运行以寻找符号链接,并ls为每个符号链接运行,只选择旧名称出现的链接。

遍历整个文件系统非常慢;你只能负担得起一次。

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

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、、symlink等。renameif debug;
  • find在子目录上运行命令,而不是/直到我确定我知道发生了什么。

小心——这是危险的东西。

于 2013-03-15T13:06:17.480 回答