Linux: zmień nazwę pliku, ale zachowaj rozszerzenie?

10

W Windows / DOS mogę powiedzieć, rename myfile.* yourfile.*żeby zmienić nazwę, ale zachowaj rozszerzenie. Jak to się robi w Linuksie?

Strona podręcznika sugeruje tylko, jak zmienić rozszerzenie, ale jest to przeciwieństwo tego, czego chcę.

Bonus:
Właściwie chcę umieścić datę utworzenia zdjęcia w nazwie pliku, aby uzyskać coś takiego 20091231 2359 New Year.jpg. Obawiam się, że potrzebuję nietrywialnej kombinacji poleceń, aby to osiągnąć?

Torben Gundtofte-Bruun
źródło

Odpowiedzi:

14

Oto odpowiedź na pytanie bonusowe.

Naprawdę chcę wstawić datę utworzenia zdjęcia do jego nazwy pliku, aby uzyskać coś w rodzaju 20091231 2359 New Year.jpg. Obawiam się, że potrzebuję nietrywialnej kombinacji poleceń, aby to osiągnąć?

Zakładając, że chcesz pobrać datę utworzenia zdjęcia z danych EXIF, potrzebujesz do tego osobnego narzędzia. Na szczęście okazuje się, że jheadoferuje trywialny sposób robienia dokładnie tego, co chcesz, z jego -nopcją.

$ 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
             [...]

Oto przykład:

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

Edycja : Oczywiście, aby zrobić to dla wielu zdjęć, byłoby to coś takiego:

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

Aby dostosować formatowanie daty do własnych upodobań, spójrz na date --helpprzykład: wyświetli listę dostępnych kodów formatu.

(jhead jest szeroko dostępny dla różnych systemów. Jeśli używasz np. Ubuntu lub Debian, po prostu wpisz, sudo apt-get install jheadaby go zainstalować.)

Jonik
źródło
1
Nie myślałem o gównie, tylko o brzydkiej kombinacji zmiany nazwy, statystyki i cięcia. Dziękuję za świetną odpowiedź!
Torben Gundtofte-Bruun
10

W przypadku części dotyczącej zmiany nazwy program „zmiana nazwy” będzie działał. Jest taki sam jak przykład, który widziałeś na stronie podręcznika, po prostu się zmieniłem.

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$ 
użytkownik23307
źródło
1
W niektórych dystrybucjach ten program zmiany nazwy perla nazywa się prename.
camh
To jest naprawdę przydatne! Byłem zaskoczony, jak wiele tego rodzaju pytań musiałem przeszukać, zanim znalazłem „zmienić nazwę”. Dziękuję Ci.
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

Kluczem jest to, że jeśli widziałeś przykład pokazujący jak zamrozić jedną część nazwy pliku za pomocą wyrażenia regularnego, jest to jedyny potrzebny przykład. Rozszerzenia nie mają specjalnego statusu w systemach plików unix - są tylko częścią nazwy pliku, która występuje po .znaku.

James Polley
źródło
1
To jest odpowiedź na moje pytanie. (Ale druga odpowiedź prowadzi mnie tam szybciej i łatwiej.)
Torben Gundtofte-Bruun
4
Nie ma potrzeby, aby widelec + exec sed: mv "$file" yourfile"${file#myfile}". Działa na dowolnej współczesnej powłoce Bourne'a (lub dowolnej powłoce POSIX), ale prawdopodobnie nie na rzeczywistej powłoce Bourne'a.
Chris Johnsen
4

Oto kilka innych sposobów manipulowania nazwami plików

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

Rozszerzenia parametrów w mvpoleceniu działają w następujący sposób:

${f%.*}- Usuń najkrótszy pasujący wzór z końca ciągu zawartego w $f, w tym przypadku usuń wszystko po ostatniej kropce włącznie. Singiel %oznacza „najkrótszy od końca”.

${f##*.}- Usuń najdłuższy pasujący wzór z początku łańcucha zawartego w $f, w tym przypadku wszystko przed ostatnią kropką i włącznie z nią (dotyczy to również innych kropek). Podwójne #( ##) oznacza „najdłużej od początku”.

Na przykład, jeśli $fzawiera „Foo.bar.baZ.jpg”:

echo "${f%.*}"

daje

Foo.bar.baZ

i

echo "${f##*.}"

daje

jpg

Tak więc mvpolecenie po rozwinięciu będzie wyglądać następująco:

mv "Foo.bar.baZ.jpg" "before_partFoo.bar.baZafter_part.jpg"
Wstrzymano do odwołania.
źródło
Skończyło się na tym, że użyłem pętli FOR, ale nie przykładu MV, ponieważ tego nie rozumiem :-)
Torben Gundtofte-Bruun
Uwaga: Znaczenie tych f%i f##są opisane tutaj, na przykład: tldp.org/LDP/abs/html/parameter-substitution.html
Torben Gundtofte-Bruun
3

W systemie Linux nie ma rozszerzeń nazw plików.

Użyj wyrażeń regularnych, aby wyciąć określone podciągi z nazwy pliku i uzyskać do nich dostęp.

Przykład:

Scenariusz z życia: wyodrębniasz html z pliku chm. W nazwach plików w systemie Windows wielkość liter nie jest rozróżniana, więc w systemie Linux będziesz mieć zepsute linki. Masz plik o nazwie index.HTML , ale href = „index.html” w adresach URL. Twoim celem jest więc dostosowanie nazw plików, aby dopasować do nich linki.

Załóżmy, że masz nazwę pliku w zmiennej:

FILENAME='index.HTML'

Począwszy od wersji 3.0 bash obsługuje same wyrażenia regularne, więc nie potrzebujesz żadnych dodatkowych narzędzi, takich jak grep / sed / perl itp., Aby wykonywać operacje na łańcuchach. Poniższy przykład ilustruje zastąpienie dopasowania zaplecza w ciągu:

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

Ciągi dopasowania i zastępcze można sparametryzować, jeśli chcesz, zapewnia to dodatkową elastyczność podczas pisania skryptu. Poniższy fragment kodu osiąga ten sam cel:

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

Zapoznaj się z dokumentacją bash, aby uzyskać dodatkowe informacje.

maniak
źródło
1
Jakiś przykład na temat tych wyrażeń regularnych?
Gnoupi
+1. Ta odpowiedź jest teraz bardzo przydatna (po edycji) - nie miałem pojęcia, że ​​możesz wykonywać takie operacje na łańcuchach w Bash.
Jonik
Rzeczywiście, teraz znacznie lepiej, z przykładami. +1
Gnoupi
0

Oto kolejna:

find -name "*.jpg" -printf '"%p" "%h/%TY%Tm%Td %TH%TM %f"\n' | while read -r f
do
    eval "mv ${f}"
done
Wstrzymano do odwołania.
źródło
0

Zawsze jest na to więcej niż jeden sposób. Umieszczam następujący skrypt jako / usr / local / bin / mrename.

Następnie w skrypcie zawierającym pliki zdjęć po prostu wpisz: mrename

W skrypcie dostępna jest również opcjonalna funkcja komentowania w celu skalowania zdjęć (za pomocą ImageMagick).

Mam nadzieję, że jest to przydatne dla niektórych osób.

#!/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";

źródło
0

Możesz użyć tej -X/--keep-extensionopcji z rename(wersja 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 jest dostępny na Maca od Homebrew i na Linuxa od Linuxbrew (oczywiście oprócz innych opcji instalacji).

Jan
źródło