Używanie seda do masowej zmiany nazw plików

89

Cel

Zmień te nazwy plików:

  • F00001-0708-RG-biasliuyda
  • F00001-0708-CS-akgdlaul
  • F00001-0708-VF-hioulgigl

do tych nazw plików:

  • F0001-0708-RG-biasliuyda
  • F0001-0708-CS-akgdlaul
  • F0001-0708-VF-hioulgigl

Kod powłoki

Testować:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/'

Występować:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/' | sh

Moje pytanie

Nie rozumiem kodu seda. Rozumiem, co to polecenie zamiany

$ sed 's/something/mv'

znaczy. I trochę rozumiem wyrażenia regularne. Ale nie rozumiem, co się tutaj dzieje:

\(.\).\(.*\)

lub tu:

& \1\2/

To pierwsze wygląda dla mnie tak, jakby oznaczało: „pojedynczy znak, po którym następuje pojedynczy znak, po którym następuje sekwencja dowolnej długości pojedynczego znaku” - ale z pewnością chodzi o coś więcej. Jeśli chodzi o drugą część:

& \1\2/

Nie mam pojęcia.

Daniel Underwood
źródło

Odpowiedzi:

152

Po pierwsze, powinienem powiedzieć, że najłatwiej to zrobić, używając poleceń prename lub rename.

W systemie Ubuntu, OSX (pakiet Homebrew, pakiet renameMacPorts p5-file-rename) lub innych systemach ze zmianą nazwy perl (prename):

rename s/0000/000/ F0000*

lub w systemach ze zmianą nazwy z util-linux-ng, takich jak RHEL:

rename 0000 000 F0000*

To o wiele bardziej zrozumiałe niż równoważne polecenie sed.

Ale jeśli chodzi o zrozumienie polecenia sed, pomocna jest strona podręcznika sed. Jeśli uruchomisz man sed i wyszukasz & (używając polecenia / do wyszukiwania), znajdziesz to specjalny znak w s / foo / bar / replaceements.

  s/regexp/replacement/
         Attempt  to match regexp against the pattern space.  If success‐
         ful,  replace  that  portion  matched  with  replacement.    The
         replacement may contain the special character & to refer to that
         portion of the pattern space  which  matched,  and  the  special
         escapes  \1  through  \9  to refer to the corresponding matching
         sub-expressions in the regexp.

Dlatego \(.\)dopasowuje pierwszy znak, do którego można się odwoływać \1. Następnie .dopasowuje następny znak, którym jest zawsze 0. Następnie \(.*\)dopasowuje resztę nazwy pliku, do której można się odwoływać \2.

Ciąg zastępujący łączy to wszystko razem za pomocą &(oryginalna nazwa pliku), \1\2która jest każdą częścią nazwy pliku z wyjątkiem drugiego znaku, który był 0.

To dość tajemniczy sposób, IMHO. Jeśli z jakiegoś powodu polecenie rename nie było dostępne i chciałbyś użyć seda do zmiany nazwy (a może robiłeś coś zbyt złożonego do zmiany nazwy?), Bardziej wyraźne wyrażenie w swoim wyrażeniu regularnym uczyniłoby go znacznie bardziej czytelnym. Może coś takiego:

ls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh

Możliwość zobaczenia, co faktycznie się zmienia w s / wyszukiwaniu / zamianie / sprawia, że ​​jest on znacznie bardziej czytelny. Ponadto nie będzie wysysać znaków z nazwy pliku, jeśli przypadkowo uruchomisz go dwa razy lub coś w tym stylu.

Edwarda Andersona
źródło
1
na moim serwerze RHEL składnia zmiany nazwy brzmiałaby „rename 0000 000 F0000 *”
David LeBauer
1
Najprawdopodobniej renamejest to link o „zmienionej nazwie” . ie renamezostała „przemianowana” z prename… np. w Ubuntu: readlink -f $(which rename)output /usr/bin/prenamerenameWspomniany przez Davida program jest zupełnie inny.
Peter. O
1
Słuszna uwaga, Peter. Zaktualizowałem odpowiedź, aby uwzględnić oba narzędzia zmiany nazwy.
Edward Anderson,
3
Aby to debugować, usuń na końcu potok do sh. Polecenia odbijają się echem na ekranie.
Ben Mathews
1
Czy na pewno dobrą radą jest przekazywanie losowych danych sh? jest to potencjalnie niebezpieczne, ponieważ można wykonać dowolny kod (traktujesz dane jako kod).
gniourf_gniourf,
46

miałeś już wyjaśnienie seda, teraz możesz używać tylko powłoki, nie potrzebujesz zewnętrznych poleceń

for file in F0000*
do
    echo mv "$file" "${file/#F0000/F000}"
    # ${file/#F0000/F000} means replace the pattern that starts at beginning of string
done
ghostdog74
źródło
1
Fajnie, ale nie możesz robić odwołań w nawiasach.
Leonidas Tsampros
28

Napisałem mały post z przykładami zmiany nazwy partii, używając sedkilka lat temu:

http://www.guyrutenberg.com/2009/01/12/batch-renaming-using-sed/

Na przykład:

for i in *; do
  mv "$i" "`echo $i | sed "s/regex/replace_text/"`";
done

Jeśli wyrażenie regularne zawiera grupy (np \(subregex\) wtedy można ich używać w tekście jako zastępczej \1\, \2itp

Chłopak
źródło
Zwróć uwagę, że odradza się udzielanie odpowiedzi zawierających tylko łącza (linki z czasem stają się nieaktualne). Rozważ edycję odpowiedzi i dodanie streszczenia tutaj.
kleopatra
nie jest tak wydajne, ale wykonuje pracę dla kilkuset plików. Głosowano za.
Varun Chandak
23

Najłatwiej byłoby:

for i in F00001*; do mv "$i" "${i/F00001/F0001}"; done

lub przenośnie,

for i in F00001*; do mv "$i" "F0001${i#F00001}"; done

Spowoduje to zastąpienie F00001przedrostka w nazwach plików przez F0001. kredyty dla mahesha tutaj: http://www.debian-administration.org/articles/150

Mikrofon
źródło
3
Powinieneś poprawnie zacytować zmienne interpolacje; mv "$i" "${i/F00001/F0001}". Ale +1
tripleee
7

sedkomenda

s/\(.\).\(.*\)/mv & \1\2/

środki do wymiany:

\(.\).\(.*\)

z:

mv & \1\2

tak jak zwykłe sedpolecenie. Jednak nawiasy &i \nznaczniki trochę to zmieniają.

Ciąg wyszukiwania dopasowuje (i zapamiętuje jako wzorzec 1) pojedynczy znak na początku, po którym następuje pojedynczy znak, po którym następuje reszta ciągu (zapamiętywany jako wzorzec 2).

W łańcuchu zastępczym możesz odwołać się do tych dopasowanych wzorców, aby użyć ich jako części zamiany. Możesz również nazywać całą dopasowaną część jako &.

Więc to sedpolecenie tworzy mvpolecenie na podstawie oryginalnego pliku (dla źródła) i znaków od 1 do 3, skutecznie usuwając znak 2 (dla miejsca docelowego). Wyświetli serię linii w następującym formacie:

mv F00001-0708-RG-biasliuyda F0001-0708-RG-biasliuyda
mv abcdef acdef

i tak dalej.

paxdiablo
źródło
1
To było dobre wyjaśnienie, ale może być przydatne wskazanie, w jaki sposób używasz polecenia sed z innymi poleceniami, aby faktycznie zmienić nazwy plików. Na przykład:ls | sed "s/\(.\).\(.*\)/mv & \1\2/" | bash
jcarballo
@jcarballo: analizowanie ls, przepuszczanie, seda następnie przepuszczanie przez powłokę jest niebezpieczne ! podlega wykonaniu dowolnego kodu ze sfałszowanymi nazwami plików. Problem polega na tym, że dane powinny być traktowane jako dane, a tutaj są one zwykle serializowane do kodu bez żadnych środków ostrożności. Chciałbym, żeby paxdiablo usunęło tę odpowiedź, ponieważ tak naprawdę nie pokazuje dobrych praktyk. (Natknąłem się na to pytanie, ponieważ początkujący losowo rurami | shpo komendzie, że nie działa i po obejrzeniu tego pytania i odpowiedzi myślałem, że to działa lepiej Jestem przerażony!) :).
gniourf_gniourf,
3

Odwrotny ukośnik-paren oznacza „dopasowując wzór, trzymaj się tego, co tutaj pasuje”. Później, po stronie tekstu zastępczego, możesz odzyskać te zapamiętane fragmenty za pomocą „\ 1” (pierwszy blok w nawiasach), „\ 2” (drugi blok) i tak dalej.

Spiczasty
źródło
1

Jeśli wszystko, co naprawdę robisz, to usuwanie drugiej postaci, niezależnie od tego, co to jest, możesz to zrobić:

s/.//2

ale twoje polecenie buduje mvpolecenie i przesyła je do powłoki w celu wykonania.

To nie jest bardziej czytelne niż twoja wersja:

find -type f | sed -n 'h;s/.//4;x;s/^/mv /;G;s/\n/ /g;p' | sh

Czwarty znak jest usuwany, ponieważ findkażda nazwa pliku jest poprzedzona znakiem „./”.

Dennis Williamson
źródło
Chciałbym, żebyś mógł usunąć tę odpowiedź. Chociaż być może było to dobre w bardzo konkretnym przypadku OP, wiele osób widzi takie odpowiedzi i ich nie rozumie, i losowo potokuje | shpo poleceniu, które nie działa, w nadziei, że zadziała lepszy. To przerażające! (a poza tym to nie jest dobra praktyka). Mam nadzieję, że zrozumiesz!
gniourf_gniourf
1

Za pomocą zmiany nazwy perla ( musi mieć w przyborniku):

rename -n 's/0000/000/' F0000*

Usuń -nprzełącznik, gdy wyjście wygląda dobrze, aby zmienić nazwę na rzeczywistość.

ostrzeżenie Istnieją inne narzędzia o tej samej nazwie, które mogą, ale nie muszą być w stanie tego zrobić, więc bądź ostrożny.

Polecenie rename, które jest częścią util-linuxpakietu, nie będzie.

Jeśli uruchomisz następujące polecenie ( GNU)

$ rename

i widzisz perlexpr, to wydaje się być właściwym narzędziem.

Jeśli nie, aby uczynić go domyślnym (zwykle już tak jest) Debiani pochodną, ​​taką jak Ubuntu:

$ sudo apt install rename
$ sudo update-alternatives --set rename /usr/bin/file-rename

Dla archlinux:

pacman -S perl-rename

Dystrybucje z rodziny RedHat:

yum install prename

Pakiet „prename” znajduje się w repozytorium EPEL .


W przypadku Gentoo:

emerge dev-perl/rename

W przypadku * BSD:

pkg install gprename

lub p5-File-Rename


Użytkownicy komputerów Mac:

brew install rename

Jeśli nie masz tego polecenia w innej dystrybucji, wyszukaj menedżera pakietów, aby go zainstalować lub zrób to ręcznie :

cpan -i File::Rename

Starą samodzielną wersję można znaleźć tutaj


zmiana nazwy człowieka


To narzędzie zostało pierwotnie napisane przez Larry'ego Wall'a, ojca Perla.

Gilles Quenot
źródło
0

Nawiasy wychwytują określone ciągi do użycia przez liczby z odwrotnym ukośnikiem.

Ewan Todd
źródło
0
 ls F00001-0708-*|sed 's|^F0000\(.*\)|mv & F000\1|' | bash
ghostdog74
źródło
Straszny! podlega wykonaniu dowolnego kodu (może nie w konkretnym kontekście pytania, ale jest wiele osób, które widzą takie odpowiedzi i próbują losowo wpisać coś, co wygląda na to, a to jest przerażające niebezpieczne!). Chciałbym, żebyś mógł usunąć tę odpowiedź (poza tym masz tutaj inną dobrą, na którą głosowałem).
gniourf_gniourf,
0

Oto, co bym zrobił:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done

Następnie, jeśli wygląda to dobrze, dodaj | shna końcu. Więc:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done | sh
Chris Po
źródło
0

Kilka przykładów, które działają dla mnie:

$ tree -L 1 -F .
.
├── A.Show.2020.1400MB.txt
└── Some Show S01E01 the Loreming.txt

0 directories, 2 files

## remove "1400MB" (I: ignore case) ...

$ for f in *; do mv 2>/dev/null -v "$f" "`echo $f | sed -r 's/.[0-9]{1,}mb//I'`"; done;
renamed 'A.Show.2020.1400MB.txt' -> 'A.Show.2020.txt'

## change "S01E01 the" to "S01E01 The"
## \U& : change (here: regex-selected) text to uppercase;
##       note also: no need here for `\1` in that regex expression

$ for f in *; do mv 2>/dev/null "$f" "`echo $f | sed -r "s/([0-9] [a-z])/\U&/"`"; done

$ tree -L 1 -F .
.
├── A.Show.2020.txt
└── Some Show S01E01 The Loreming.txt

0 directories, 2 files
$ 
Victoria Stuart
źródło
-1
for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done
user3164360
źródło
4
Witamy w SO. Proszę rozważyć dodanie wyjaśnienia swojego kodu. Pomoże to innym użytkownikom w zrozumieniu tego.
Digvijay S
Ta odpowiedź jest dobra, ale jest prawie identyczną odpowiedzią powyżej wysoko ocenianej odpowiedzi.
Eric Leschinski