简体   繁体   中英

how to change names of all directories / files containing a specific string on linux

I have a directory where I want to change all of the directory names and files under it to a different name. For example my directory structure is

./mydir_ABC/
./mydir_ABC/myfile_ABC.txt
./mydir_ABC/otherdir_ABC/

and I want to make it for example

./mydir_DEF/
./mydir_DEF/myfile_DEF.txt
./mydir_DEF/otherdir_DEF/

I am using

find . -type 'f' -name '*ABC*'
find . -type 'd' -name '*ABC*'

to get a list of all the relevant directories and files that contain my string in their names. How do I pipe it to another command that will actually change the directory names and file names? (I want it to be able to overwrite if the file or directory already exists.)

mkdir z; cd z; touch fooabcbar; touch fooABCbar
find . -type 'f' \( -name '*ABC*' -o -name '*abc*' \) | while read f; do g=`echo "$f" | sed -e 's/abc/def/g' -e 's/ABC/DEF/g'`; echo mv -- "$f" "$g"; done

As long as the pathname doesn't include a newline (very rare).

Look up the rename(1) command. There are some different variations on it, but they all support some sort of renaming based on regular expressions of some sort. The version I use (based on code from the first edition of the Perl 'Camel Book') would be used as:

rename 's%ABC%DEF%g' ...

Or, for your example:

find . -print0 | xargs -0 rename s/ABC/DEF/g

Or, to avoid problems with path names changing while you're traversing the directory structure, do the directory names first, then the files (as in the question):

find . -type d -print0 | xargs -0 rename s/ABC/DEF/g
find . -type f -print0 | xargs -0 rename s/ABC/DEF/g

There could still be problems with renaming directories as you go (because once you've renamed ./ABC to ./DEF , you can no longer rename ./ABC/subABC/ to ./DEF/subDEF because the directory is now ./DEF/subABC ).

Other versions of rename have different syntaxes - check your manual pages.


Here is the Perl-based version of rename derived from the first edition of the Perl 'Camel Book'; the first version I have under version control has a date of 1992-01-05. Today's change updated the shebang line (uses env, removed -w since it is not reliable, and added use warnings; to compensate for no -w ). Prior changes were 2008, 1998, 1996 and 1992; it hasn't changed much over the last 19 years.

#!/usr/bin/env perl
#
# @(#)$Id: rename.pl,v 1.8 2011/06/03 22:30:22 jleffler Exp $
#
# Rename files using a Perl substitute or transliterate command

use strict;
use warnings;
use Getopt::Std;

my(%opts);
my($usage) = "Usage: $0 [-fnxV] perlexpr [filenames]\n";
my($force) = 0;
my($noexc) = 0;
my($trace) = 0;

die $usage unless getopts('fnxV', \%opts);

if ($opts{V})
{
    printf "%s\n", q'RENAME Version $Revision: 1.8 $ ($Date: 2011/06/03 22:30:22 $)';
    exit 0;
}
$force = 1 if ($opts{f});
$noexc = 1 if ($opts{n});
$trace = 1 if ($opts{x});

my($op) = shift;
die $usage unless defined $op;

if (!@ARGV) {
    @ARGV = <STDIN>;
    chop(@ARGV);
}

for (@ARGV)
{
    if (-e $_ || -l $_)
    {
        my($was) = $_;
        eval $op;
        die $@ if $@;
        next if ($was eq $_);
        if ($force == 0 && -f $_)
        {
            print STDERR "rename failed: $was - $_ exists\n";
        }
        else
        {
            print "+ $was --> $_\n" if $trace;
            print STDERR "rename failed: $was - $!\n"
                unless ($noexc || rename($was, $_));
        }
    }
    else
    {
        print STDERR "$_ - $!\n";
    }
}

If it's always the last 3 characters this should work well enough I would think:

mv *abc.* *def.*

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM