Mam wiele plików uporządkowanych według nazw plików w katalogu. Chcę skopiować ostatnie N (powiedzmy N = 4) plików do mojego katalogu domowego. Jak mam to zrobić?
W przypadku wszystkich poniższych odpowiedzi może być konieczne dodanie „LC_ALL = ...” przed poleceniami korzystającymi z zakresów, aby ich zakresy używały odpowiednich ustawień regionalnych (ustawienia narodowe mogą się zmieniać, a następnie kolejność znaków może się zmieniać np. w niektórych lokalizacjach [az] może zawierać wszystkie litery alfabetu oprócz „Z”, ponieważ litery są uporządkowane: aAbBcC .... zZ. Istnieją inne warianty (litery akcentowane i jeszcze bardziej egzotyczne uporządkowanie Zwykłym wyborem jest:LC_ALL=C
Olivier Dulac
Odpowiedzi:
23
Można to łatwo zrobić za pomocą tablic bash / ksh93 / zsh:
a=(*)
cp --"${a[@]: -4}"~/
Działa to w przypadku wszystkich nie ukrytych nazw plików, nawet jeśli zawierają spacje, tabulatory, znaki nowej linii lub inne trudne znaki (zakładając, że w bieżącym katalogu są co najmniej 4 pliki nie ukryte bash).
Jak to działa
a=(*)
Spowoduje to utworzenie tablicy aze wszystkimi nazwami plików. Nazwy plików zwrócone przez bash są posortowane alfabetycznie. (Zakładam, że to właśnie rozumiesz przez „uporządkowane według nazwy pliku” ).
${a[@]: -4}
Zwraca ostatnie cztery elementy tablicy a(pod warunkiem, że tablica zawiera co najmniej 4 elementy z bash).
cp -- "${a[@]: -4}" ~/
Spowoduje to skopiowanie czterech ostatnich nazw plików do katalogu domowego.
Aby skopiować i zmienić nazwę jednocześnie
Spowoduje to skopiowanie tylko czterech ostatnich plików do katalogu domowego, a jednocześnie dopisanie łańcucha a_do nazwy skopiowanego pliku:
Jeśli użyjemy a=(./some_dir/*)zamiast a=(*), mamy problem z dołączeniem katalogu do nazwy pliku. Jednym z rozwiązań jest:
a=(./some_dir/*)for f in"${a[@]: -4}";do cp "$f"~/a_"${f##*/}";done
Innym rozwiązaniem jest użycie podpowłoki i cdkatalogu w podpowłoce:
(cd ./some_dir && a=(*)&&for f in"${a[@]: -4}";do cp --"$f"~/a_"$f";done)
Po zakończeniu podpowłoki powłoka wraca do oryginalnego katalogu.
Upewnienie się, że zamówienie jest spójne
Pytanie dotyczy plików „uporządkowanych według nazwy pliku”. To zamówienie, zauważa Olivier Dulac w komentarzach, będzie się różnić w zależności od regionu. Jeśli ważne jest, aby ustalone wyniki były niezależne od ustawień komputera, najlepiej jest wyraźnie określić ustawienia narodowe, gdy tablica ajest zdefiniowana. Na przykład:
LC_ALL=C a=(*)
Możesz dowiedzieć się, w którym aktualnie się znajdujesz, uruchamiając localepolecenie.
Jeśli używasz zsh, możesz zawrzeć w nawiasie ()listę tak zwanych globalnych kwalifikatorów, które wybierają pożądane pliki. W twoim przypadku tak byłoby
cp *(On[1,4])~/
Tutaj Onsortuje nazwy plików alfabetycznie w odwrotnej kolejności i [1,4]zajmuje tylko pierwsze 4 z nich.
Można zrobić to bardziej wytrzymałe wybierając tylko zwykłe pliki (z wyłączeniem katalogów, itp) z rur ., a także poprzez dołączenie --do cpkomendy w celu traktują pliki, których nazwy rozpoczynają się -prawidłowo, więc:
cp --*(.On[1,4])~
Dodaj Dkwalifikator, jeśli chcesz również uwzględnić ukryte pliki (pliki kropek):
@ StéphaneChazelas tak, pozwala wyjaśnić przyszłym czytelnikom, że sortowanie alfabetyczne w normalnym kierunku ( on) jest zachowaniem domyślnym, więc nie trzeba tego określać, a -przed liczbami nazwy plików liczą od dołu.
jimmij
7
Oto rozwiązanie wykorzystujące niezwykle proste polecenia bash.
Jest to bardzo podobne do bashoferowanego rozwiązania macierzy -specyficznego, ale powinno być przenośne dla dowolnej powłoki kompatybilnej z POSIX. Kilka uwag na temat obu metod:
Ważne jest, abyś poprowadził swoje cpargumenty w / cp --lub dostał jedną z .kropek lub /na początku każdego imienia. Jeśli tego nie zrobisz, ryzykujesz pierwszym -myślnikiem cppierwszego argumentu, który może być interpretowany jako opcja i powodować niepowodzenie operacji lub w inny sposób generować niezamierzone wyniki.
Nawet jeśli pracujesz z bieżącym katalogiem jako katalogiem źródłowym, łatwo to zrobić tak jak ... set -- ./*lub array=(./*).
Ważne jest, aby przy użyciu obu metod upewnić się, że masz przynajmniej tyle elementów w tablicy arg, ile próbujesz usunąć - robię to tutaj z rozszerzeniem matematycznym. shiftTylko się dzieje, jeśli istnieją co najmniej 4 pozycji w tablicy arg - i to tylko te pierwsze zmiany oddalony args które sprawiają, że nadwyżkę 4 ..
Więc w set 1 2 3 4 5przypadku 1odsunie to, ale w set 1 2 3przypadku niczego nie zmieni.
Na przykład: a=(1 2 3); echo "${a[@]: -4}"drukuje pustą linię.
Jeśli kopiujesz z jednego katalogu do drugiego, możesz użyć pax:
Jeśli są tylko pliki, a ich nazwy nie zawierają białych znaków ani znaków nowej linii (a nie zmodyfikowałeś $IFS) ani znaków globalnych (lub niektórych znaków niedrukowalnych z pewnymi implementacjami ls) i nie zaczynaj od ., możesz to zrobić :
Dzięki! To działa. Czy istnieje możliwość szybkiego dodania prefiksu a_do WSZYSTKICH czterech plików? Próbowałem echo "a_$(ls | tail -n 4)", ale poprzedza tylko pierwszy plik.
Sibbs Gambling
@SibbsGambling Moje podejście nie jest do tego odpowiednie, ale odpowiedź John1024 można łatwo dostosować do tego.
Hauke Laging
5
Ogólnie parsowanie lsdanych wyjściowych jest uważane za złą formę, ponieważ zwykle strasznie zawodzi w nazwach plików zawierających nie tylko spacje, ale także tabulatory, znaki nowej linii lub inne prawidłowe, ale „trudne” znaki.
HBruijn
2
@HaukeLaging Nawet jeśli obecnie nie stanowi to problemu (ponieważ nie mam „skomplikowanych” nazw ogniowych w moim katalogu), mogę to zrobić za 2 lata i nagle coś mi spada na nogi ...
glglgl
1
To proste rozwiązanie bash bez kodu pętli. Skopiuj ostatnie 4 pliki z bieżącego katalogu do miejsca docelowego:
Jak się upewnić, że finddaje to pliki w pożądanej kolejności? Jak upewnić się, że pliki zawierające znaki białych znaków (w tym znaki nowej linii) w ich nazwach są obsługiwane poprawnie?
LC_ALL=C
Odpowiedzi:
Można to łatwo zrobić za pomocą tablic bash / ksh93 / zsh:
Działa to w przypadku wszystkich nie ukrytych nazw plików, nawet jeśli zawierają spacje, tabulatory, znaki nowej linii lub inne trudne znaki (zakładając, że w bieżącym katalogu są co najmniej 4 pliki nie ukryte
bash
).Jak to działa
a=(*)
Spowoduje to utworzenie tablicy
a
ze wszystkimi nazwami plików. Nazwy plików zwrócone przez bash są posortowane alfabetycznie. (Zakładam, że to właśnie rozumiesz przez „uporządkowane według nazwy pliku” ).${a[@]: -4}
Zwraca ostatnie cztery elementy tablicy
a
(pod warunkiem, że tablica zawiera co najmniej 4 elementy zbash
).cp -- "${a[@]: -4}" ~/
Spowoduje to skopiowanie czterech ostatnich nazw plików do katalogu domowego.
Aby skopiować i zmienić nazwę jednocześnie
Spowoduje to skopiowanie tylko czterech ostatnich plików do katalogu domowego, a jednocześnie dopisanie łańcucha
a_
do nazwy skopiowanego pliku:Skopiuj z innego katalogu, a także zmień nazwę
Jeśli użyjemy
a=(./some_dir/*)
zamiasta=(*)
, mamy problem z dołączeniem katalogu do nazwy pliku. Jednym z rozwiązań jest:Innym rozwiązaniem jest użycie podpowłoki i
cd
katalogu w podpowłoce:Po zakończeniu podpowłoki powłoka wraca do oryginalnego katalogu.
Upewnienie się, że zamówienie jest spójne
Pytanie dotyczy plików „uporządkowanych według nazwy pliku”. To zamówienie, zauważa Olivier Dulac w komentarzach, będzie się różnić w zależności od regionu. Jeśli ważne jest, aby ustalone wyniki były niezależne od ustawień komputera, najlepiej jest wyraźnie określić ustawienia narodowe, gdy tablica
a
jest zdefiniowana. Na przykład:Możesz dowiedzieć się, w którym aktualnie się znajdujesz, uruchamiając
locale
polecenie.źródło
Jeśli używasz
zsh
, możesz zawrzeć w nawiasie()
listę tak zwanych globalnych kwalifikatorów, które wybierają pożądane pliki. W twoim przypadku tak byłobyTutaj
On
sortuje nazwy plików alfabetycznie w odwrotnej kolejności i[1,4]
zajmuje tylko pierwsze 4 z nich.Można zrobić to bardziej wytrzymałe wybierając tylko zwykłe pliki (z wyłączeniem katalogów, itp) z rur
.
, a także poprzez dołączenie--
docp
komendy w celu traktują pliki, których nazwy rozpoczynają się-
prawidłowo, więc:Dodaj
D
kwalifikator, jeśli chcesz również uwzględnić ukryte pliki (pliki kropek):źródło
*([-4,-1])
.on
) jest zachowaniem domyślnym, więc nie trzeba tego określać, a-
przed liczbami nazwy plików liczą od dołu.Oto rozwiązanie wykorzystujące niezwykle proste polecenia bash.
Wyjaśnienie:
Znajduje wszystkie pliki w bieżącym katalogu.
Sortuje alfabetycznie.
Pokaż tylko 4 ostatnie wiersze.
Pętle w każdej linii wykonują polecenie kopiowania.
źródło
Dopóki uznasz, że sortowanie powłoki jest przyjemne, możesz po prostu:
Jest to bardzo podobne do
bash
oferowanego rozwiązania macierzy -specyficznego, ale powinno być przenośne dla dowolnej powłoki kompatybilnej z POSIX. Kilka uwag na temat obu metod:Ważne jest, abyś poprowadził swoje
cp
argumenty w /cp --
lub dostał jedną z.
kropek lub/
na początku każdego imienia. Jeśli tego nie zrobisz, ryzykujesz pierwszym-
myślnikiemcp
pierwszego argumentu, który może być interpretowany jako opcja i powodować niepowodzenie operacji lub w inny sposób generować niezamierzone wyniki.set -- ./*
lubarray=(./*)
.Ważne jest, aby przy użyciu obu metod upewnić się, że masz przynajmniej tyle elementów w tablicy arg, ile próbujesz usunąć - robię to tutaj z rozszerzeniem matematycznym.
shift
Tylko się dzieje, jeśli istnieją co najmniej 4 pozycji w tablicy arg - i to tylko te pierwsze zmiany oddalony args które sprawiają, że nadwyżkę 4 ..set 1 2 3 4 5
przypadku1
odsunie to, ale wset 1 2 3
przypadku niczego nie zmieni.a=(1 2 3); echo "${a[@]: -4}"
drukuje pustą linię.Jeśli kopiujesz z jednego katalogu do drugiego, możesz użyć
pax
:... który zastosowałby
sed
podstawienie w stylu do wszystkich nazw plików podczas ich kopiowania.źródło
Jeśli są tylko pliki, a ich nazwy nie zawierają białych znaków ani znaków nowej linii (a nie zmodyfikowałeś
$IFS
) ani znaków globalnych (lub niektórych znaków niedrukowalnych z pewnymi implementacjamils
) i nie zaczynaj od.
, możesz to zrobić :źródło
a_
do WSZYSTKICH czterech plików? Próbowałemecho "a_$(ls | tail -n 4)"
, ale poprzedza tylko pierwszy plik.ls
danych wyjściowych jest uważane za złą formę, ponieważ zwykle strasznie zawodzi w nazwach plików zawierających nie tylko spacje, ale także tabulatory, znaki nowej linii lub inne prawidłowe, ale „trudne” znaki.To proste rozwiązanie bash bez kodu pętli. Skopiuj ostatnie 4 pliki z bieżącego katalogu do miejsca docelowego:
źródło
find
daje to pliki w pożądanej kolejności? Jak upewnić się, że pliki zawierające znaki białych znaków (w tym znaki nowej linii) w ich nazwach są obsługiwane poprawnie?