Rename multiple files based on pattern in Unix

前端 未结 22 921
死守一世寂寞
死守一世寂寞 2020-11-22 06:31

There are multiple files in a directory that begin with prefix fgh, for example:

fghfilea
fghfileb
fghfilec

I want to rename a

相关标签:
22条回答
  • 2020-11-22 06:53

    There are several ways, but using rename will probably be the easiest.

    Using one version of rename:

    rename 's/^fgh/jkl/' fgh*
    

    Using another version of rename (same as Judy2K's answer):

    rename fgh jkl fgh*
    

    You should check your platform's man page to see which of the above applies.

    0 讨论(0)
  • 2020-11-22 06:54

    Using find, xargs and sed:

    find . -name "fgh*" -type f -print0 | xargs -0 -I {} sh -c 'mv "{}" "$(dirname "{}")/`echo $(basename "{}") | sed 's/^fgh/jkl/g'`"'
    

    It's more complex than @nik's solution but it allows to rename files recursively. For instance, the structure,

    .
    ├── fghdir
    │   ├── fdhfilea
    │   └── fghfilea
    ├── fghfile\ e
    ├── fghfilea
    ├── fghfileb
    ├── fghfilec
    └── other
        ├── fghfile\ e
        ├── fghfilea
        ├── fghfileb
        └── fghfilec
    

    would be transformed to this,

    .
    ├── fghdir
    │   ├── fdhfilea
    │   └── jklfilea
    ├── jklfile\ e
    ├── jklfilea
    ├── jklfileb
    ├── jklfilec
    └── other
        ├── jklfile\ e
        ├── jklfilea
        ├── jklfileb
        └── jklfilec
    

    The key to make it work with xargs is to invoke the shell from xargs.

    0 讨论(0)
  • 2020-11-22 07:02

    This script worked for me for recursive renaming with directories/file names possibly containing white-spaces:

    find . -type f -name "*\;*" | while read fname; do
        dirname=`dirname "$fname"`
        filename=`basename "$fname"`
        newname=`echo "$filename" | sed -e "s/;/ /g"`
        mv "${dirname}/$filename" "${dirname}/$newname"
    done
    

    Notice the sed expression which in this example replaces all occurrences of ; with space . This should of course be replaced according to the specific needs.

    0 讨论(0)
  • 2020-11-22 07:02

    I would recommend using my own script, which solves this problem. It also has options to change the encoding of the file names, and to convert combining diacriticals to precomposed characters, a problem I always have when I copy files from my Mac.

    #!/usr/bin/perl
    
    # Copyright (c) 2014 André von Kugland
    
    # Permission is hereby granted, free of charge, to any person obtaining a
    # copy of this software and associated documentation files (the "Software"),
    # to deal in the Software without restriction, including without limitation
    # the rights to use, copy, modify, merge, publish, distribute, sublicense,
    # and/or sell copies of the Software, and to permit persons to whom the
    # Software is furnished to do so, subject to the following conditions:
    
    # The above copyright notice and this permission notice shall be included in
    # all copies or substantial portions of the Software.
    
    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    # DEALINGS IN THE SOFTWARE.
    
    $help_msg =
    "rename.pl, a script to rename files in batches, using Perl
               expressions to transform their names.
    Usage:
        rename.pl [options] FILE1 [FILE2 ...]
    Where options can be:
        -v                      Verbose.
        -vv                     Very verbose.
        --apply                 Really apply modifications.
        -e PERLCODE             Execute PERLCODE. (e.g. 's/a/b/g')
        --from-charset=CS       Source charset. (e.g. \"iso-8859-1\")
        --to-charset=CS         Destination charset. (e.g. \"utf-8\")
        --unicode-normalize=NF  Unicode normalization form. (e.g. \"KD\")
        --basename              Modifies only the last element of the path.
    ";
    
    use Encode;
    use Getopt::Long;
    use Unicode::Normalize 'normalize';
    use File::Basename;
    use I18N::Langinfo qw(langinfo CODESET);
    
    Getopt::Long::Configure ("bundling");
    
    # ----------------------------------------------------------------------------------------------- #
    #                                           Our variables.                                        #
    # ----------------------------------------------------------------------------------------------- #
    
    my $apply = 0;
    my $verbose = 0;
    my $help = 0;
    my $debug = 0;
    my $basename = 0;
    my $unicode_normalize = "";
    my @scripts;
    my $from_charset = "";
    my $to_charset = "";
    my $codeset = "";
    
    # ----------------------------------------------------------------------------------------------- #
    #                                        Get cmdline options.                                     #
    # ----------------------------------------------------------------------------------------------- #
    
    $result = GetOptions ("apply" => \$apply,
                          "verbose|v+" => \$verbose,
                          "execute|e=s" => \@scripts,
                          "from-charset=s" => \$from_charset,
                          "to-charset=s" => \$to_charset,
                          "unicode-normalize=s" => \$unicode_normalize,
                          "basename" => \$basename,
                          "help|h|?" => \$help,
                          "debug" => \$debug);
    
    # If not going to apply, then be verbose.
    if (!$apply && $verbose == 0) {
      $verbose = 1;
    }
    
    if ((($#scripts == -1)
      && (($from_charset eq "") || ($to_charset eq ""))
      && $unicode_normalize eq "")
      || ($#ARGV == -1) || ($help)) {
      print $help_msg;
      exit(0);
    }
    
    if (($to_charset ne "" && $from_charset eq "")
      ||($from_charset eq "" && $to_charset ne "")
      ||($to_charset eq "" && $from_charset eq "" && $unicode_normalize ne "")) {
      $codeset = langinfo(CODESET);
      $to_charset = $codeset if $from_charset ne "" && $to_charset eq "";
      $from_charset = $codeset if $from_charset eq "" && $to_charset ne "";
    }
    
    # ----------------------------------------------------------------------------------------------- #
    #         Composes the filter function using the @scripts array and possibly other options.       #
    # ----------------------------------------------------------------------------------------------- #
    
    $f = "sub filterfunc() {\n    my \$s = shift;\n";
    $f .= "    my \$d = dirname(\$s);\n    my \$s = basename(\$s);\n" if ($basename != 0);
    $f .= "    for (\$s) {\n";
    $f .= "        $_;\n" foreach (@scripts);   # Get scripts from '-e' opt. #
    # Handle charset translation and normalization.
    if (($from_charset ne "") && ($to_charset ne "")) {
      if ($unicode_normalize eq "") {
        $f .= "        \$_ = encode(\"$to_charset\", decode(\"$from_charset\", \$_));\n";
      } else {
        $f .= "        \$_ = encode(\"$to_charset\", normalize(\"$unicode_normalize\", decode(\"$from_charset\", \$_)));\n"
      }
    } elsif (($from_charset ne "") || ($to_charset ne "")) {
        die "You can't use `from-charset' nor `to-charset' alone";
    } elsif ($unicode_normalize ne "") {
      $f .= "        \$_ = encode(\"$codeset\", normalize(\"$unicode_normalize\", decode(\"$codeset\", \$_)));\n"
    }
    $f .= "    }\n";
    $f .= "    \$s = \$d . '/' . \$s;\n" if ($basename != 0);
    $f .= "    return \$s;\n}\n";
    print "Generated function:\n\n$f" if ($debug);
    
    # ----------------------------------------------------------------------------------------------- #
    #                 Evaluates the filter function body, so to define it in our scope.               #
    # ----------------------------------------------------------------------------------------------- #
    
    eval $f;
    
    # ----------------------------------------------------------------------------------------------- #
    #                  Main loop, which passes names through filters and renames files.               #
    # ----------------------------------------------------------------------------------------------- #
    
    foreach (@ARGV) {
      $old_name = $_;
      $new_name = filterfunc($_);
    
      if ($old_name ne $new_name) {
        if (!$apply or (rename $old_name, $new_name)) {
          print "`$old_name' => `$new_name'\n" if ($verbose);
        } else {
          print "Cannot rename `$old_name' to `$new_name'.\n";
        }
      } else {
        print "`$old_name' unchanged.\n" if ($verbose > 1);
      }
    }
    
    0 讨论(0)
  • 2020-11-22 07:03

    It was much easier (on my Mac) to do this in Ruby. Here are 2 examples:

    # for your fgh example. renames all files from "fgh..." to "jkl..."
    files = Dir['fgh*']
    
    files.each do |f|
      f2 = f.gsub('fgh', 'jkl')
      system("mv #{f} #{f2}")
    end
    
    # renames all files in directory from "021roman.rb" to "021_roman.rb"
    files = Dir['*rb'].select {|f| f =~ /^[0-9]{3}[a-zA-Z]+/}
    
    files.each do |f|
      f1 = f.clone
      f2 = f.insert(3, '_')
      system("mv #{f1} #{f2}")
    end
    
    0 讨论(0)
  • 2020-11-22 07:03

    This worked for me using regexp:

    I wanted files to be renamed like this:

    file0001.txt -> 1.txt
    ofile0002.txt -> 2.txt 
    f_i_l_e0003.txt -> 3.txt
    

    usig the [a-z|_]+0*([0-9]+.) regexp where ([0-9]+.) is a group substring to use on the rename command

    ls -1 | awk 'match($0, /[a-z|\_]+0*([0-9]+.*)/, arr) { print   arr[0]  " "  arr[1] }'|xargs  -l mv
    

    Produces:

    mv file0001.txt 1.txt
    mv ofile0002.txt 2.txt
    mv f_i_l_e0003.txt 3.txt
    

    Another example:

    file001abc.txt -> abc1.txt
    ofile0002abcd.txt -> abcd2.txt 
    
    ls -1 | awk 'match($0, /[a-z|\_]+0*([0-9]+.*)([a-z]+)/, arr) { print   arr[0]  " "  arr[2] arr[1] }'|xargs  -l mv
    

    Produces:

      mv file001abc.txt abc1.txt
      mv ofile0002abcd.txt abcd2.txt 
    

    Warning, be careful.

    0 讨论(0)
提交回复
热议问题