Linux: renomeie o arquivo, mas mantenha a extensão?

10

No Windows / DOS, posso dizer rename myfile.* yourfile.*para mudar o nome, mas manter a extensão. Como isso é realizado no Linux?

A página de manual sugere apenas como alterar a extensão, mas é o oposto do que eu quero.

Bônus:
Na verdade, quero colocar a data de criação de uma foto em seu nome de arquivo, para obter algo parecido 20091231 2359 New Year.jpg. Receio precisar de uma combinação não trivial de comandos para conseguir isso?

Torben Gundtofte-Bruun
fonte

Respostas:

14

Aqui está uma resposta para a pergunta do bônus.

Na verdade, eu quero colocar a data de criação de uma foto em seu nome de arquivo, para obter algo como 20091231 2359 New Year.jpg. Receio precisar de uma combinação não trivial de comandos para conseguir isso?

Supondo que você queira tirar a data de criação da foto a partir dos dados EXIF, precisará de uma ferramenta separada para isso. Felizmente, isso jheadoferece uma maneira trivial de fazer exatamente o que você deseja, com sua -nopção.

$ jhead -h
 [...]

 -n[format-string]

             Rename files according to date.  Uses exif date if present, file
             date otherwise.  If the optional format-string is not supplied,
             the format is mmdd-hhmmss.  If a format-string is given, it is
             is passed to the 'strftime' function for formatting
             In addition to strftime format codes:
             '%f' as part of the string will include the original file name
             [...]

Aqui está um exemplo:

$ jhead -n%Y-%m-%d-%f New_year.jpg   
New_year.jpg --> 2009-12-31-New_year.jpg

Edit : Claro, para fazer isso em várias fotos, seria algo como:

$ for i in *jpg; do jhead -n%Y-%m-%d-%f $i; done

Para ajustar a formatação da data ao seu gosto, dê uma olhada na saída de date --help, por exemplo; listará os códigos de formato disponíveis.

(O jhead está amplamente disponível para diferentes sistemas. Se você está no Ubuntu ou Debian, digite simplesmente sudo apt-get install jheadpara instalá-lo.)

Jonik
fonte
11
Eu não tinha pensado em jhead, apenas em uma feia combinação de renomear, stat e cut. Obrigado pela ótima resposta!
Torben Gundtofte-Bruun 01/01
10

Apenas para a parte de renomeação, o programa 'renomear' funcionará. É o mesmo que o exemplo que você viu na página de manual, apenas mudou.

justin@eee:/tmp/q$ touch myfile.{a,b,c,d}
justin@eee:/tmp/q$ ls
myfile.a  myfile.b  myfile.c  myfile.d
justin@eee:/tmp/q$ rename -v s/myfile/yourfile/ myfile.*
myfile.a renamed as yourfile.a
myfile.b renamed as yourfile.b
myfile.c renamed as yourfile.c
myfile.d renamed as yourfile.d
justin@eee:/tmp/q$ ls
yourfile.a  yourfile.b  yourfile.c  yourfile.d
justin@eee:/tmp/q$ 
user23307
fonte
11
Em algumas distros, esse programa de renomeação de perl é chamado prename.
Camh
Isso é realmente útil! Fiquei surpreso com quantos desses tipos de perguntas eu tive que pesquisar antes de encontrar "renomear". Obrigado.
9186 alanning
7
betelgeuse:tmp james$ ls myfile.* yourfile.*
ls: yourfile.*: No such file or directory   
myfile.a    myfile.b
betelgeuse:tmp james$ for file
> in myfile.*
> do
> mv "${file}" "`echo $file | sed 's/myfile\./yourfile./'`"
> done
betelgeuse:tmp james$ ls myfile.* yourfile.*
ls: myfile.*: No such file or directory
yourfile.a  yourfile.b

A chave é que, se você viu um exemplo que mostra como mover uma parte do nome do arquivo com uma expressão regular, esse é o único exemplo que você precisa. As extensões não têm status especial nos sistemas de arquivos unix - elas são apenas uma parte do nome do arquivo que está atrás de um .caractere.

James Polley
fonte
11
Esta é a resposta para minha pergunta. (Mas a outra resposta me chega lá mais rápido e mais fácil.)
Torben Gundtofte-Bruun
4
Não há necessidade de garfo + exec sed: mv "$file" yourfile"${file#myfile}". Funciona em qualquer shell moderno tipo Bourne (ou qualquer shell POSIX), mas provavelmente não o shell Bourne real.
Chris Johnsen
4

Aqui estão mais algumas maneiras diferentes de manipular nomes de arquivos

for f in *.jpg
do
    mv "$f" "before_part${f%.*}after_part.${f##*.}"
    # OR mv "$f" "before_part$(basename "$f" ".jpg")after_part.jpg"
done

As expansões de parâmetro no mvcomando funcionam da seguinte maneira:

${f%.*}- Exclua o padrão de correspondência mais curto do final da string contida $f, neste caso, exclua tudo depois e incluindo o último ponto. O single %significa "o mais curto do final".

${f##*.}- Exclua o padrão de correspondência mais longo do início da string contida $f, neste caso, tudo antes e incluindo o último ponto (isso inclui outros pontos também). O duplo #( ##) significa "mais longo desde o início".

Então, por exemplo, se $fcontém "Foo.bar.baZ.jpg":

echo "${f%.*}"

Foo.bar.baZ

e

echo "${f##*.}"

jpg

Portanto, o mvcomando, uma vez expandido, seria semelhante a:

mv "Foo.bar.baZ.jpg" "before_partFoo.bar.baZafter_part.jpg"
Pausado até novo aviso.
fonte
Na verdade, eu acabei usando o loop PARA mas não o exemplo MV porque eu não entendo isso :-)
Torben Gundtofte-Bruun
Nota: O significado desses itensf% e f##é descrito aqui, por exemplo: tldp.org/LDP/abs/html/parameter-substitution.html
Torben Gundtofte-Bruun
3

Não há extensões de nome de arquivo no Linux.

Use expressões regulares para cortar substrings específicos do nome do arquivo e acessá-los.

Exemplo:

Cenário da vida real: você está extraindo html de um arquivo chm. Os nomes de arquivos no Windows não diferenciam maiúsculas de minúsculas; portanto, no Linux, você obterá links quebrados. Você tem um arquivo chamado index.HTML , mas href = "index.html" nos URLs. Portanto, seu objetivo é adaptar os nomes de arquivos para corresponder aos links para eles.

Suponha que você tenha o nome do arquivo em uma variável:

FILENAME='index.HTML'

A partir da versão 3.0, o bash suporta expressões regulares, portanto você não precisa de ferramentas adicionais, como grep / sed / perl, etc. para executar a manipulação de strings. O exemplo a seguir ilustra a substituição de uma correspondência de backend em uma sequência:

echo ${FILENAME/%\.HTML/.html}

As seqüências de correspondência e substituição podem ser parametrizadas, se você desejar, isso fornece flexibilidade adicional ao escrever um script. O seguinte snippet de código atinge o mesmo objetivo:

match='\.HTML'
replacement='.html'
echo ${FILENAME/%$match/$replacement}

Consulte os documentos do bash para obter informações adicionais.

nerd
fonte
11
Algum exemplo, nessas expressões regulares?
Gnoupi
+1. Esta resposta é muito útil agora (após a edição) - eu não tinha ideia de que você pode fazer essa manipulação de strings no Bash.
Jonik
De fato, muito melhor agora, com exemplos. +1
Gnoupi 6/01/10
0

Aqui está outro:

find -name "*.jpg" -printf '"%p" "%h/%TY%Tm%Td %TH%TM %f"\n' | while read -r f
do
    eval "mv ${f}"
done
Pausado até novo aviso.
fonte
0

Sempre há mais de uma maneira de fazer isso. Coloquei o seguinte script como / usr / local / bin / mrename.

Em seguida, no script que contém os arquivos de foto, digite: mrename

Há também um recurso opcional comentado no script para dimensionar as fotos (usando o ImageMagick).

Espero que isso seja útil para algumas pessoas.

#!/usr/bin/perl
#
# mrename files
#
#
use strict;

# if no 2 args, use defaults
my $dir = ".";

# read in path from command line
$dir = $ARGV[0] if ( defined( $ARGV[0] ) && $ARGV[0] ne "" );

# read in directory contents
opendir( DIR, $dir );
my @files = readdir( DIR );
closedir( DIR );

# rename and/or scale each file in directory
my $number_of_files = scalar( @files );
my $curfile = 0;

foreach my $file( @files ) {
    # only rename and scale jpg/gif files
    if ( $file =~ /\w+\.(jpg)$/ ) {
        my $extension = $1;
        $extension =~ tr/A-Z/a-z/;
        my $full_filename = "$dir/$file";

        # get stats on file- specifically the last modified time
        (my $dev,my $ino,my $mode,my $nlink,my $uid,my $gid,my $rdev,my $size,
        my $atime,my $mtime,my $ctime,my $blksize,my $blocks) = stat($full_filename);

        # convert last-modified time from seconds to practical datetime terms
        (my $sec,my $min,my $hour,my $mday,my $mon,my $year,my $wday,my $yday,
        my $isdst) = localtime($mtime);

        ++$mon;
        $year += 1900;

        my $filecdate = sprintf( "m%04i%02i%02i_%02i%02i%02i.$extension", $year, $mon, $mday, $hour, $min, $sec );
        my $full_newfilename = "$dir/$filecdate";

        # to scale files, use imagemagick by using the command below instead of mv 
        #my $cmd = "convert $full_filename -resize $scale% $full_newfilename";
        my $cmd = "mv $full_filename $full_newfilename";
        system( $cmd );

        # update percentage done
        my $percent_done = sprintf( "%5.2lf", 100* (++$curfile) / $number_of_files );
        print "\r$percent_done%";
    }
}
print "\n";

fonte
0

Você pode usar a -X/--keep-extensionopção com rename(versão 1.600):

-X, --keep-extension Save and remove the last extension from a filename, if there is any. The saved extension will be appended back to the filename at the end of the rest of the operations.

rename está disponível para Mac a partir do Homebrew e para Linux a partir do Linuxbrew (entre outras opções de instalação, é claro).

John
fonte