Powiedzmy, że chcę skopiować zawartość katalogu z wyjątkiem plików i folderów, których nazwy zawierają słowo „Muzyka”.
cp [exclude-matches] *Music* /target_directory
Co powinno zostać zastąpione przez [wykluczające mecze], aby to osiągnąć?
Powiedzmy, że chcę skopiować zawartość katalogu z wyjątkiem plików i folderów, których nazwy zawierają słowo „Muzyka”.
cp [exclude-matches] *Music* /target_directory
Co powinno zostać zastąpione przez [wykluczające mecze], aby to osiągnąć?
Odpowiedzi:
W Bash można to zrobić poprzez włączenie extglob
opcji, jak to (wymienić ls
z cp
i dodać katalog docelowy, oczywiście)
~/foobar> shopt extglob
extglob off
~/foobar> ls
abar afoo bbar bfoo
~/foobar> ls !(b*)
-bash: !: event not found
~/foobar> shopt -s extglob # Enables extglob
~/foobar> ls !(b*)
abar afoo
~/foobar> ls !(a*)
bbar bfoo
~/foobar> ls !(*foo)
abar bbar
Możesz później wyłączyć extglob za pomocą
shopt -u extglob
f
, oprócz foo
?
Opcja extglob
powłoki zapewnia mocniejsze dopasowanie wzorców w wierszu poleceń.
Włączasz za pomocą shopt -s extglob
i wyłączasz za pomocą shopt -u extglob
.
W twoim przykładzie początkowo zrobiłbyś:
$ shopt -s extglob
$ cp !(*Music*) /target_directory
Pełne dostępny ext zakończył glob operatorzy Bing są (fragment man bash
):
Jeśli opcja powłoki extglob jest włączona przy użyciu wbudowanego shopt, rozpoznawanych jest kilka rozszerzonych operatorów dopasowywania wzorców. Lista wzorów jest listą jednego lub więcej wzorów oddzielonych | Wzory złożone mogą być tworzone przy użyciu jednego lub więcej następujących pod-wzorów:
- ? (lista wzorców)
Dopasowuje zero lub jedno wystąpienie podanych wzorów- * (lista wzorców)
Dopasowuje zero lub więcej wystąpień podanych wzorów- + (lista wzorców)
Dopasowuje jedno lub więcej wystąpień podanych wzorów- @ (lista wzorów)
Dopasowuje jeden z podanych wzorów- ! (lista wzorców)
Dopasowuje wszystko oprócz jednego z podanych wzorów
Na przykład, jeśli chcesz wyświetlić listę wszystkich plików w bieżącym katalogu, które nie są .c
lub .h
pliki, wykonaj następujące czynności:
$ ls -d !(*@(.c|.h))
Oczywiście działa normalne globowanie powłoki, więc ostatni przykład można również zapisać jako:
$ ls -d !(*.[ch])
.c
lub .h
jest katalogiem.
D
, ale nie zawartość podkatalogów, które mogą się w nim znajdować D
.
Nie w bashu (o którym wiem), ale:
cp `ls | grep -v Music` /target_directory
Wiem, że nie było to dokładnie to, czego szukałeś, ale to rozwiąże twój przykład.
Jeśli chcesz uniknąć kosztów memu korzystania z polecenia exec, uważam, że lepiej poradzisz sobie z xargs. Myślę, że poniższe jest bardziej wydajną alternatywą dla
find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec
find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/
W bash alternatywą shopt -s extglob
jest GLOBIGNORE
zmienna . To nie jest naprawdę lepsze, ale łatwiej mi zapamiętać.
Przykładem może być to, czego chciał oryginalny plakat:
GLOBIGNORE="*techno*"; cp *Music* /only_good_music/
Po zakończeniu , unset GLOBIGNORE
aby móc to zrobić rm *techno*
w katalogu źródłowym.
Możesz także użyć dość prostej for
pętli:
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
-maxdepth 1
do nierekurencyjnych?
find
w backtickach spowoduje nieprzyjemne działanie, jeśli znajdzie jakieś nietrywialne nazwy plików.
Osobiście wolę używać grep i komendy while. Pozwala to pisać potężne, ale czytelne skrypty, zapewniając, że skończysz robić dokładnie to, co chcesz. Ponadto za pomocą polecenia echa można wykonać próbę przed rozpoczęciem rzeczywistej operacji. Na przykład:
ls | grep -v "Music" | while read filename
do
echo $filename
done
wydrukuje pliki, które skończysz kopiować. Jeśli lista jest poprawna, następnym krokiem jest po prostu zastąpienie polecenia echo poleceniem kopiowania w następujący sposób:
ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done
bash
można użyć while IFS='' read -r filename
, ale wtedy nowe linie są nadal problemem. Zasadniczo najlepiej nie używać ls
do wyliczania plików; narzędzia takie find
są znacznie lepiej dostosowane.
for file in *; do case ${file} in (*Music*) ;; (*) cp "${file}" /target_directory ; echo ;; esac; done
Sztuczka Nie widziałem tu jeszcze, że nie używa extglob
, find
czy grep
jest w leczeniu dwie listy plików jak zestawy i „diff” je za pomocą comm
:
comm -23 <(ls) <(ls *Music*)
comm
jest lepszy niż, diff
ponieważ nie ma dodatkowego cruft.
Zwraca wszystkie elementy zestawu 1 ls
, które również nie znajdują się w zestawie 2 ls *Music*
,. Wymaga to, aby oba zestawy były posortowane, aby działały poprawnie. Nie ma problemu ls
i globalnej ekspansji, ale jeśli używasz czegoś podobnego find
, pamiętaj, aby wywołać sort
.
comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)
Potencjalnie przydatne.
comm -23 <(ls) exclude_these.list
Jedno rozwiązanie tego można znaleźć w find.
$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt
Znajdź ma wiele opcji, możesz uzyskać dość szczegółowe informacje na temat tego, co dołączasz i wykluczasz.
Edytuj: Adam w komentarzach zauważył, że jest to rekurencyjne. znajdź opcje mindepth i maxdepth mogą być przydatne w kontrolowaniu tego.
find -maxdepth 1 -not -name '*Music*'
/
Poniższe prace zawierają listę wszystkich *.txt
plików w bieżącym katalogu, z wyjątkiem plików zaczynających się na liczbę.
To działa w bash
, dash
, zsh
a wszystkie inne kompatybilne muszle POSIX.
for FILE in /some/dir/*.txt; do # for each *.txt file
case "${FILE##*/}" in # if file basename...
[0-9]*) continue ;; # starts with digit: skip
esac
## otherwise, do stuff with $FILE here
done
W pierwszym wierszu wzorzec /some/dir/*.txt
spowoduje for
iterację pętli po wszystkich plikach, /some/dir
których nazwa kończy się na .txt
.
W drugim wierszu zastosowano instrukcję case, aby usunąć niepożądane pliki. - ${FILE##*/}
Wyrażenie usuwa wszystkie wiodące komponenty nazwy katalogu z nazwy pliku (tutaj /some/dir/
), dzięki czemu wzory mogą pasować tylko do nazwy basena pliku. (Jeśli usuwasz tylko nazwy plików na podstawie sufiksów, możesz $FILE
zamiast tego skrócić ).
W linii trzeciej wszystkie pliki pasujące do case
wzorca [0-9]*
zostaną pominięte ( continue
instrukcja przeskoczy do następnej iteracji for
pętli). - Jeśli chcesz, możesz zrobić coś bardziej interesującego, np. Pominąć wszystkie pliki, które nie zaczynają się na literę (a – z) [!a-z]*
, lub możesz użyć wielu wzorców, aby pominąć kilka rodzajów nazw plików, np. [0-9]*|*.bak
Pominąć pliki oba .bak
pliki oraz pliki, które nie zaczynają się od liczby.
*.txt
zamiast po prostu *
). Naprawiono teraz.
to zrobiłoby to wykluczając dokładnie „Muzykę”
cp -a ^'Music' /target
to i to za wykluczenie takich rzeczy jak Muzyka? * lub *? Muzyka
cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target
cp
podręcznika w systemie MacOS ma -a
opcję, ale robi coś zupełnie innego. Która platforma to obsługuje?
ls /dir/*/!(base*)