tło
W systemie Linux możesz:
- Wyświetl grupę plików za pomocą
ls Filename*
- Usuń grupę plików za pomocą
rm Filename*
- Przenieś grupę plików za pomocą
mv Filename* /New/Directory
- Ale nie można skopiować grupy plików za pomocą:
cp Filename* *.bak
Zmień cp
komendę Linux, aby skopiować grupę plików
Mam grupę plików, które chciałbym skopiować bez wprowadzania nazw jeden po drugim i używania cp
polecenia:
$ ls gmail-meta3*
gmail-meta3 gmail-meta3-REC-1558392194-26467821
gmail-meta3-LAB-1558392194-26467821 gmail-meta3-YAD-1558392194-26467821
Jak mogę użyć czegoś takiego jak stare polecenie DOS copy gmail-meta3* *.bak
?
Nie chcę wpisywać podobnego polecenia cztery razy:
cp gmail-meta3-LAB-1558392194-26467821 gmail-meta3-LAB-1558392194-26467821.bak
Szukam skryptu / funkcji / aplikacji, która akceptuje parametry dla starej i nowej grupy nazw plików, a nie coś z zakodowanymi nazwami plików. Na przykład użytkownik może wpisać:
copy gmail-meta3* *.bak
lub mogą wpisać:
copy gmail-meta3* save-*
command-line
cp
ms-dos
WinEunuuchs2Unix
źródło
źródło
touch aa ab ba; mkdir bb; cp a* b*; ls *
*
dla nazw plików źródłowych, ale nie dla nazw plików docelowych. Jako taki, jak zastępczy symbol zastępczy (myślę, że##
został zasugerowany, ale pochylam się w kierunku%
), będzie musiał zostać użyty jako cel. Myślę, że to właśnie wzmacniasz? Nie spodziewałem sięcp
w ogóle zmienić polecenia. Wystarczy utworzyć skrypt otoki,copy
który naśladuje (w granicach rozsądku) polecenie kopiowania DOS.Odpowiedzi:
Oto przykład jednego nietypowego użycia
sed
, które ma zastosowanie do tego zadania:W ten sposób faktycznie
sed
nie zmieni plików, ponieważ nie dostarczyliśmy żadnych poleceń''
, ale dzięki opcji-i[suffix]
utworzy kopię zapasową każdego pliku. Znalazłem to podejście, kiedy szukałem. Czy istnieje sposób na utworzenie kopii zapasowej pliku, bez dwukrotnego wpisania jego nazwy?źródło
$ time sed -i.bak '' gmail-meta3*
=real 0m0.069s
real 0m0.037s
. Jeśli pliki usunięte i uruchomić po raz drugi bliskocp
prędkości:real 0m0.051s
.sed
jest szybszy, gdy pliki docelowe już istnieją, alecp
jest wolniejszy, gdy istnieją pliki docelowe. Właściwie opróżniam pamięć podręczną i bufory, a niesync
robię duże testy czasowe, ale tym razem nie zrobiłem tego. Ponieważ może to zagłębić się w zupełnie inną mydlaną operę bez pytania, z przykrością udostępniam wyniki moich testów :( Czy jest już za późno, by udawać, że ta rozmowa nigdy się nie wydarzyła? Rozmiar metadanych wiadomości Gmaila wynosi 2,5 MB, a 3 pliki indeksu mają około 800 KB Ponadto nie jest to dysk twardy, to dysk SSD Samsung Pro 960 NVMe na 4 kanałachfree
polecenia. Rzeczywiste zapisy w urządzeniu mają miejsce w momencie wybranym przez algorytm, który uwzględnia wiek pamięci podręcznej i nacisk pamięci na maszynie. Jeśli próbujesz wykonać wiele testów, to pierwsze odczyty plików wyjdą z dysku, ale kolejne odczyty najprawdopodobniej wyjdą z pamięci (patrzsync; echo 3 > /proc/sys/vm/drop_caches
).cp
polecenia, ale nie mogę powiedzieć, że istnieje znacząca różnica w wydajności .Możesz użyć
find
:To znajdzie w bieżącym katalogu
.
wszystkie pliki o nazwie pasującej do wzorca globu (pamiętaj o pojedynczych cudzysłowach wokół wzorca, aby zapobiec globowaniu powłoki). Dla każdego znalezionego pliku będzie on wykonywanycp
od nazwy do nazwy.bak. \; na końcu zapewnia, że zrobi każdy plik osobno, zamiast przekazywać je wszystkie naraz. Maksymalna głębokość, ponieważ 1 przeszukuje katalog cuurent zamiast rekurencji w dół.źródło
find . -max-depth 1 -name '"$1"'' -exec cp "{}" "{}$2" \;
gdy 1 USD to źródło, a 2 USD to rozszerzenie?Możesz użyć
for
pętli zbash
. Zwykle po prostu wpisałbym go jako linijkę, ponieważ nie jest to zadanie, które często wykonuję:Jeśli jednak potrzebujesz go jako skryptu:
Użycie jest podobne do
rename
. Testować:źródło
$ time for f in gmail-meta3* ; do cp -a "$f" "${f}.bak" ; done
=real 0m0.046s
0.046
sekundy, co oznacza 0 sekund dla ludzkiej percepcji. Próbowałem tylko pokazać, w jaki sposób testuję opublikowane odpowiedzi i przekazuję ciekawe ciekawostki widzom, którzy spojrzeli nased
powyższe polecenie. Albo przynajmniej nie był zainteresowany w porównaniused
docp
....cp
jest jednak szybsze niż rozwiązanie zsed
. To jest powód do świętowania :)-a
jest niestandardowym operatorem testowym. Dlaczego nie użyć-e
? (2) „Nie można utworzyć katalogu tymczasowego.” Jest nieco mylącym komunikatem o błędzie. (3) Dlaczego nie po prostu użyćmktemp -d
? (4) Powinieneś przetestować status wyjścia. Na przykład powinieneś powiedzieć! mkdir "$FOLDER" && echo "Unable to create temporary directory." && return 1
lubmkdir "$FOLDER" || { echo "Unable to create temporary directory."; return 1;}
. Podobnie dlacp
irename
(a może nawetpushd
, jeśli chcesz być ostrożny). … (Ciąg dalszy)$@
; powiedzieć"$@"
. (5b) Nie ma potrzeby używania,{
a}
kiedy odwołujesz się do zmiennych, tak jak robisz ("${FOLDER}"
,"${PATTERN}"
i"${file}"
); po prostu zrobić"$FOLDER"
,"$PATTERN"
i"$file"
. (6) Zakłada się, że pliki znajdują się w bieżącym katalogu.cps 's/$/.bak/' d/foo
skopiujed/foo
dofoo.bak
bieżącego katalogu, a nied/foo.bak
.Najbliżej prawdopodobnie dostaniesz się do paradygmatu DOS
mcp
(zmmv
pakietu):Jeśli
zsh
jest dostępny, to jegozmv
moduł może być nieco bliżej:Unikałbym
ls
niezależnie - wariant na własną odpowiedź, który jest bezpieczny dla białych znaków (w tym nowych linii)a może
źródło
mmv
to pakiet, ale w komentarzach mówisz, że polecenie jest,mcp
ale potem w poleceniu, którego używasz,mmv
które jest również poleceniem wmmv
pakiecie. Podoba mi się kierunekprintf
przykładów i w dopracowanym skrypcie zapewniłbym przekazanie 1 $ i 2 $. +1 za rozpoczęcie toczenia piłki :)mcp
jest tylko synonimemmmv -c
printf
polecenie, którego tak naprawdę nigdy nie użyłem. Czy mówisz,printf '%s\0' "$1"*
że działałoby, gdybygmail-meta3
został przekazany jako parametr 1?cps gmail-meta3*
a następnie napisałemprintf '%s\0
„$ @” | podczas gdy ... w funkcji. Lub po prostu użyjfor f; do cp -- "$f" "$f.bak"; done
(jak odpowiedź Xiota , ale jako funkcja)zmv
że możesz użyć trybu „zastępowania symboli wieloznacznych”, cozmv -W -C 'gmail-meta3*' '*.bak'
tylko rozwiązanie rsync
Jeśli chcesz tylko wykonać kopię zapasową plików, możesz skopiować je do nowego katalogu
rsync /path/to/dir/Filename* /path/to/backupdirectory
Spowoduje to skopiowanie
Filename
plików z/path/to/dir/
do/path/to/backupdirectory
.rsync + nazwa pliku
Jeśli chcesz, aby pliki kopii zapasowych miały sufiks, rzeczy stają się hacked z
rsync
...rsync -Iu /path/to/dir/Filename* /path/to/dir/Filename* -b --backup-dir=/path/to/backupdirectory --suffix=.bak
Spowodowałoby to zastąpienie istniejących plików ... istniejącymi plikami (
-I
), ale tylko wtedy, gdy są (-u
) nowsze (które nie są) i utworzenie kopii zapasowej z przyrostkiem.Możesz to również zrobić w tym samym katalogu. Lepiej jednak wyklucz istniejące kopie zapasowe.
rsync -Iu /path/to/dir/Filename* /path/to/dir/Filename* -b --backup-dir=/path/to/backupdirectory --suffix=.bak --exclude '*.bak'
źródło
rsycnc
więc głosowałem, ale prostszą metodą byłoby,cp Filename* /path/to/backup/dir
ponieważ pliki nie potrzebowałyby*.bak
unikalizatora, gdyby znajdowały się w osobnym katalogu.Ten należy wykonać zgodnie z żądaniem:
Stosowanie:
Wybrałem,
?
ponieważ#
musi być cytowany, gdy jest to pierwszy znak wzoru, w przeciwnym razie zostanie rozpoznany jako początek komentarza.Test:
Niektóre wcześniejsze wersje skryptu do dodawania sufiksu:
Podobne do odpowiedzi @ WinEunuuchs2Unix, ale myślę, że jest bardziej elastyczny i nie analizuje
ls
:Umieść to w swoim
.bashrc
.Stosowanie:
Alternatywnie, z sufiksem jako ostatnim argumentem ( via i via ):
Stosowanie:
źródło
copy gmail-meta3* old-meta3*
. W mojej odpowiedzi nie mogłem wymyślić, jak dostać*
się do nazwy docelowej, tak jak moje pytanie ...*
jest interpretowany przez powłokę, więc funkcja nie będzie o tym wiedzieć. Potrzebujesz innego znaku lub go zacytujesz, a następnie zastąp go oryginalną nazwą pliku w funkcji.#
można go użyć jako zamiennika wieloznacznego dla*
? Więc możesz pisaćcopy filenames# save-#
. Myślę, że chcesz, aby znak wieloznaczny był taki sam dla źródła i celu.Napisałem ten linijkę do siebie
~/.bashrc
.find
Przypuszczam, że można opublikować znacznie lepsze odpowiedzi przy użyciu . Jeszcze lepsze odpowiedzi można napisać w C. Mam nadzieję, że te pytania i odpowiedzi sprawią, że otrzymamy lepsze odpowiedzi:for f in "$1"*; do
:$1
togmail-meta3
parametr if
lista pasujących plików. W połączeniu oznacza to dla gmail-meta3, gmail-meta3-LAB-9999 itp. Wykonaj następujące czynności[[ ! "$f" == *"$2" ]] &&
:$f
jest taki sam jakf
powyżej.$2
to.bak
parametr przekazany. W połączeniu oznacza to, że jeśli nazwa pliku nie kończy się na.bak
(ponieważ nie chcemy kopiować.bak
i tworzyć.bak.bak
), wykonaj następujące czynnościcp -a "$f" "$f$2";
skopiuj gmail-meta3 na gmail-meta3.bak itp.done
: zapętlić i pobrać następną nazwę pliku zgmail-meta3
listy *.cps gmail-meta3 .bak
Przykładowe dane wyjściowePrzykładem tego pytania jest to, jak wygląda on w działaniu:
Uwaga: Używa
-a
flagi zcp
poleceniem, aby zachować znaczniki czasu i lepiej zrozumieć kopie zapasowe plików.Zwróć uwagę, że kopie plików mają dokładnie tę samą datę i godzinę co oryginały. Jeśli
-a
parametr zostanie pominięty, otrzymają aktualną datę i godzinę i nie wyglądają jak prawdziwa kopia zapasowa, z wyjątkiem tego, że rozmiar pliku byłby taki sam.źródło
ls
find
kiedy wspomniałeś , zakładam, że zdajesz sobie sprawę z niebezpieczeństwa parsowanials
? Ale w twoim przypadku nie jest to konieczne: po prostu zróbfor file in "$1"*; do copy -a "$file" "$file$2"; done
to - jest to całkowicie bezpieczne i znacznie prostsze niż jakikolwiek rodzaj pośrednictwa za pośrednictwemls
lubfind
iwhile
pętli.Inną metodą spełnienia wymagań jest skopiowanie plików do katalogu tymczasowego i
rename
zmiana nazwy za pomocą polecenia.Jeśli potrzebujesz go jako skryptu, możesz go użyć w ten sposób
Możesz go używać w następujący sposób:
To jest przykład
źródło