BASH: Trabalhando com vários arquivos e diretórios

3

Eu tenho cerca de 15 lotes de mais de 100 diretórios, especificamente usando pseudocódigo como você pode:

rename * *suffix  
rename * prefix*  
rename * CAPITALIZE*  

MAS dicas ou links para tutoriais específicos para trabalhar com vários arquivos e diretórios seriam bem-vindos.

    
por Ande TURNER 31.10.2009 / 03:47

6 respostas

5

Muitos sistemas linux / unix vêm com um rename ou rename.pl ou prename perl-script que permite renomear via expressões perl. Nos sistemas Debian, ele é instalado com o pacote perl .

$ rename
Usage: rename [-v] [-n] [-f] perlexpr [filenames]

A mágica acontece no argumento perlexpr . Este é essencialmente um pequeno pedaço de código perl que irá operar em cada nome de arquivo. Você não precisa fornecer nenhum loop, apenas escreva um código que transforme sua entrada na saída desejada. (Certifique-se e cite a expressão; eu uso aspas simples para proteger contra a expansão do shell, a menos que seja absolutamente necessário.)

Então, para seus exemplos:

  • renomear * * sufixo

    $ rename 's/$/suffix/' *
              ^ ^  ^^^^
              | |    +-- your suffix goes here
              | +------- indicates end-of-string
              +--------- perl's substitution operator (s/from-regex/to/)
    
  • renomear * prefixo *

    $ rename 's/^/prefix/' *
                ^  ^^^^
                |    +-- your prefix goes here
                +------- indicates beginning-of-string
    
  • renomeie * CAPITALIZAR *

    $ rename '$_ = uc' *
              ^^^^ ^^
               |    +-- perl builtin function to capitalize a string
               +------- replaces input filename
    

Veja mais exemplos do livro da O'Reilly Ferramentas de energia do Unix .

    
por 31.10.2009 / 05:44
2

Isso funcionará para adições de prefixo e sufixo a todos os nomes de arquivo.

find /base/dir/path -type f -exec mv {} prefix{}suffix \;
#                                    -- ^^^^^^--^^^^^^

Para a capitalização, você provavelmente poderia escrever um pequeno loop bash que usaria algo como sed para alterar o caso de letras e, em seguida, mover.

Vou sugerir leitura,

  1. Guile avançado de script Bash
  2. Perl scripting
por 31.10.2009 / 04:08
2

Eu usaria Perl - e especificamente esta versão modificada de um script 'rename' que estava na 1ª edição do livro Camel (mas caiu da segunda e terceira edições por falta de espaço).

Uso:

find . -type f -print0 | xargs -0 rename 's%/([^/]+)$%/prefix\U${1}\Esuffix%'

O que, sendo traduzido, significa:

  • encontre todos os arquivos, mesmo aqueles com espaços em branco em seus nomes, e os manipule adequadamente.
  • para cada nome de arquivo, encontre o nome após a última barra, lembrando-o como '$ {1}'
  • substitua o nome por "prefixo", a versão em maiúsculas do que foi lembrado e "sufixo"

Assim, todo o trabalho pode ser feito em um comando com uma ferramenta suficientemente versátil.

#!/Users/jleffler/perl/v5.10.0/bin/perl -w
#
# @(#)$Id: rename.pl,v 1.7 2008/02/16 07:53:08 jleffler Exp $
#
# Rename files using a Perl substitute or transliterate command

use strict;
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.7 $ ($Date: 2008/02/16 07:53:08 $)';
    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";
    }
}

Por tudo o que vale a pena, me deparei com uma falha persistente que não foi terrivelmente informativa quando tentei:

$ find . -type f -print0 | xargs -0 rename 's/.*/prefix$&suffix/'
rename failed: ./xxx-32 - No such file or directory
rename failed: ./xxx-64 - No such file or directory
rename failed: ./xxx.c - No such file or directory
rename failed: ./xxx.c~ - No such file or directory
$

Isso foi estranho - eu posso ver esses arquivos ...

Usar a opção '-x' para 'renomear' me disse qual era o problema:

rename failed: ./xxx-32 - No such file or directory
rename failed: ./xxx-64 - No such file or directory
rename failed: ./xxx.c - No such file or directory
rename failed: ./xxx.c~ - No such file or directory
+ ./xxx-32 --> prefix./xxx-32suffix
+ ./xxx-64 --> prefix./xxx-64suffix
+ ./xxx.c --> prefix./xxx.csuffix
+ ./xxx.c~ --> prefix./xxx.c~suffix

Ah, certo - não existe um subdiretório chamado 'prefixo'. no meu diretório atual!

    
por 31.10.2009 / 05:52
1

Para as maiúsculas e minúsculas, que tal:

for filename in *                # Traverse all files in directory.
do
   fname='basename $filename'
   n='echo $fname | tr A-Z a-z'  # Change name to lowercase.
   if [ "$fname" != "$n" ]       # Rename only files not already lowercase.
   then
     mv $fname $n
   fi  
done   

Roubado descaradamente do Projeto de Documentação do Linux: link

Tem que ser mais fácil (e com menos recursos) do que usar o Perl.

    
por 31.10.2009 / 06:13
0

Eu gosto mais do Perl rename , se você tiver ou puder obtê-lo.

Aqui estão algumas variações de MAIÚSCULAS:

# take out the echo to make these do the work

# find + xargs + bash (v4) + mv
# requires bash 4 for the ^^ uppercasing feature
find * -type f -print0 | xargs -0 bash -c 'for f in "$@"; do dir="";case "$1" in (*/*) dir="${f%/*}/";;esac;filename="${f##*/}"; echo mv "$f" "$dir${filename^^}"; done' mv

# find + xargs + sh + dirname + basename + tr + mv
# maybe easier to understand than above, but noticeably slower due to 3 forks per file
find * -type f -print0 | xargs -0 sh -c 'for f in "$@"; do echo mv "$f" "$(dirname "$f")/$(printf "$(basename "$f")" | tr "[:lower:]" "[:upper:]")"; done' mv

Eu ando brincando com zsh ultimamente, então aqui estão as invocações zmv :

# If the command line shell is not zsh, the prefix each of these commands with
#    zsh -fc 'autoload -U zmv && zmv $@'
# take the -n off to make it do the work instead of just talk about it

zmv -Qn '(**/)(*)(.)' '${1}prefix${2}'
zmv -Qn '(**/)(*)(.)' '${1}${2}suffix'
zmv -Qn '(**/)(*)(.)' '${1}${(U)2}'
# without the (.) qualifier (and the enabling -Q), dirs are processed also
zmv -n '(**/)(*)' '${1}prefix${2}'
zmv -n '(**/)(*)' '${1}${2}suffix'
zmv -n '(**/)(*)' '${1}${(U)2}'
    
por 31.10.2009 / 12:26
0

Esse script rename perl avalia o primeiro argumento como código para renomear os arquivos fornecidos com o restante dos argumentos.

#!/usr/bin/perl

($op = shift) || die "Usage: $0 perlexpr [filenames]\n";
if (!@ARGV) {
    @ARGV = <STDIN>;
    chop(@ARGV);
}
for (@ARGV) {
    $was = $_;
    eval $op;
    die $@ if $@;
    rename($was,$_) unless $was eq $_;
}
    
por 20.05.2014 / 07:29

Tags