Jak skopiować wiele plików za pomocą symboli wieloznacznych?

42

Mam folder z wieloma plikami ABC. * (Istnieje około 100 takich plików). Chcę skopiować je wszystkie do nowych plików o nazwach rozpoczynających się od DEF. *

Więc chcę

ABC.Page1
ABC.Page2
ABC.Topic12
...etc

skopiowane do

DEF.Page1
DEF.Page2
DEF.Topic12
...etc

Jaki jest najprostszy sposób, aby to zrobić za pomocą polecenia wsadowego (w BASH lub podobnym)? Myślę o czymś związanym z sed, awk lub xargs, ale mam trudności z ustaleniem składni. Mógłbym napisać skrypt w języku Python, ale myślę, że prawdopodobnie istnieje rozwiązanie w linii poleceń, które nie jest zbyt skomplikowane.

Ośmiornica
źródło
Czcigodny mcmenedżer plików ma najbardziej rozbudowane wsparcie dla tego rodzaju masowych nazw.
Oakad
@oakad też tarnie jest wiosennym kurczakiem. Zobacz moją odpowiedź na przykład.
mikeserv

Odpowiedzi:

41

Co powiesz na coś takiego w bash:

for file in ABC.*; do cp "$file" "${file/ABC/DEF}";done

możesz to przetestować, umieszczając echo przed poleceniem cp:

for file in ABC.*; do echo cp "$file" "${file/ABC/DEF}";done
Magnus
źródło
doskonały. nawet prostsze niż sobie wyobrażałem.
Ośmiornica
W większości przypadków będzie wystarczająco dobry, ale sprawdzenie za pomocą echa jest tutaj kluczowe. Nigdy nie rób tego w skrypcie . Aby uzyskać więcej informacji przeczytaj pułapki bash
pawel7318
@ pawel7318, podany przez ciebie link, choć doskonała rada, nie wydaje się mieć zastosowania do polecenia podanego powyżej. Jedynymi przypadkami krawędzi, które widzę za pomocą tego polecenia, są: (1) Nie sprawdza, czy plik docelowy już istnieje, ani czy plik docelowy już istnieje jako katalog (który skopiowałby ten plik do katalogu, zachowując jego bieżącą nazwę), oraz (2) jeśli istnieje tyle plików, że rozszerzenie glob nie może zmieścić się w jednym wierszu poleceń.
Wildcard,
1
Rozumiem: cp /usr/local/lib/libboost* /usr/local/lib/libboost*z kontrolą echa.
Tomáš Zato
8

Aby to zrobić skutecznie w przypadku dużej liczby plików, lepiej jest unikać uruchamiania cpdla każdego innego procesu. Jednym ze sposobów byłoby skopiowanie, a następnie zmiana nazwy przy użyciu prename( renamedomyślnie jest to dowiązanie symboliczne do dystrybucji opartych na Debianie). Korzystanie z tego i systemu Linux mktemp:

tmp=$(mktemp -d --tmpdir=.)
cp ABC.* "$tmp"
prename "s:$tmp/ABC:DEF:" "$tmp/"*
rmdir "$tmp"

Aktualizacja

Właściwie paxmoże być lepszym sposobem, aby przejść tutaj:

pax -rws '/^ABC/DEF/' ABC.* .

Niestety, mimo że paxjest to zarówno POSIX, jak i Linux Standard Base, obecnie niewiele dystrybucji domyślnie go zawiera.

Graeme
źródło
miły dotyk za pomocą: jako separatora, aby uniknąć ucieczki.
Clayton Stanley
5

tar zrobi to dla ciebie naprawdę szybko.

TEST

Najpierw stworzyłem 2 katalogi i 10 plików:

% mkdir test1 test2 ; cd test1
% for n in `seq 1 10` ; do touch ABC.file$n ; done
% ls

> ABC.file1   ABC.file2  ABC.file4  ABC.file6  ABC.file8
> ABC.file10  ABC.file3  ABC.file5  ABC.file7  ABC.file9

Następnie skopiowałem je:

% tar -cf - ./* |\ 
    tar -C../test2 --transform='s/ABC/DEF/' -xf -
% ls ../test2

> DEF.file1   DEF.file2  DEF.file4  DEF.file6  DEF.file8
> DEF.file10  DEF.file3  DEF.file5  DEF.file7  DEF.file9

PRZEKSZTAŁCAĆ

Dlatego GNU tarzaakceptuje sed --transform=EXPRESSIONzmianę nazwy pliku. Może to nawet zmienić nazwę tylko niektórych plików. Na przykład:

% tar -cf - ./* |\ 
    tar -C../test2 --transform='s/ABC\(.*[0-5]\)/DEF\1/' -xf -
% ls ../test2

> ABC.file6  ABC.file8  DEF.file1   DEF.file2  DEF.file4
> ABC.file7  ABC.file9  DEF.file10  DEF.file3  DEF.file5

To jedna zaleta.

STRUMIEŃ

Weź również pod uwagę, że są to tylko dwa tarprocesy - i to się nie zmieni bez względu na liczbę plików.

tar | tar

tarjest tak zoptymalizowany, jak tylko chcesz. To nigdy nie będzie miało problemów z liczeniem argumentów lub niekontrolowanymi procesami potomnymi. To jest po prostu zrobione A> B.

ARGUMENTY

Używam tutaj 7 różnych argumentów połączonych między moimi dwoma tarprocesami. Najważniejszy wymieniono tutaj jako pierwszy:

-stdout / stdin - informuje tar, że będzie przesyłać strumieniowo dane wejściowe lub wyjściowe do lub z stdin/stdoutktórych będzie poprawnie interpretować w zależności od tego, czy buduje, czy rozpakowuje archiwum.

-cutwórz - mówi, taraby zbudować archiwum. Następny argument taroczekuje ...

-fplik - określamy, że tarbędzie działał z fileobiektem, a nie z urządzeniem taśmowym lub czymkolwiek. Plik, z którym będzie współpracował, jak wspomniano powyżej, jest stdin/stdout- innymi słowy, nasz |pipe.

./*wszystkie $ PWD / pliki - nie za dużo do wyjaśnienia tutaj, z wyjątkiem tego, że argument archiwum jest na pierwszym miejscu, więc -wtedy ./*.

... a po drugiej stronie |pipe...

-Czmień katalog - informuje tar, że musi przejść do katalogu, który podam przed wykonaniem jakiejkolwiek innej akcji, a więc skutecznie cd ../test2przed rozpakowaniem.

--transform='s/ed/EXPR/'- jak już wspomniano, zmieniła nazwę. Ale dokumenty wskazują, że może przyjąć dowolny sedwyraz lub //flag.

-xwyodrębnij - po tarzmianach w naszym katalogu docelowym i otrzymaniu instrukcji zmiany nazwy instruujemy go, aby rozpoczął rozpakowywanie wszystkich plików do bieżącego katalogu z -f - |pipepliku archiwum. Bez tajemnicy.

mikeserv
źródło
1
Oto paxrozwiązanie tego pytania. Chciałbym dodać to do mojej odpowiedzi, ale myślę, że lepiej tu pasuje -pax -rws '/^ABC/DEF/' ABC.* .
Graeme
@Graeme - woah - to o wiele lepsze! Człowieku, nazwa pereł też bije piekło! Powinieneś umieścić to w swojej odpowiedzi - paxjest określony POSIX - stąd pax.
mikeserv
paxmoże być POSIX, ale niestety rzadko jest instalowany domyślnie.
Graeme
4

Jeśli nie masz nic przeciwko użyciu mniej standardowego narzędzia, polecam użycie mmv (ruch masowy), który został napisany do tego rodzaju zadań. Używając mcp(kopii masowej), która jest częścią pakietu, możesz po prostu to zrobić

mcp "ABC.*" "DEF.#1"

Nie jest łatwo znaleźć pakiety do pobrania. Aktualny pakiet Debiana (którego Arch używa również w AUR) do kompilacji w domu można znaleźć tutaj .

jonny
źródło
instaluje się na Ubuntu 15.10 po wyjęciu z pudełka,apt-get install mmv
Thufir
2

W Zsh wstaw następujące wiersze do swojego .zshrc:

autoload -U zmv
alias zmv='noglob zmv'
alias zcp='zmv -C'
alias zln='zmv -L'

Następnie w wierszu poleceń możesz uruchomić

zcp 'ABC(.*)' 'DEF$1'

lub po prostu

zcp -W ABC.* DEF.*

W innej powłoce, o ile masz zainstalowany Zsh:

zsh -c 'autoload zmv; noglob zmv -CW ABC.* DEF.*'
Gilles „SO- przestań być zły”
źródło
1

Możesz to zrobić:

for i in ABC.*; do cp {ABC,DEF}."${i#*.}"; done
Sara Fauzia
źródło
Może jest coś, czego nie widzę, ale jak ustawić wiele miejsc docelowych? Korzystam z pętli for, aby uzyskać każdą z nich. Testowałem wcześniej kod i działał on zgodnie z oczekiwaniami, więc byłbym wdzięczny za twoje wyjaśnienie.
Sara Fauzia,
To moje złe, źle odczytałem twoją odpowiedź. :-)
Chris Down
Jak mam to przeczytać "${i#*.}"?
dumbledad