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 extglobopcji, jak to (wymienić lsz cpi 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 extglobpowłoki zapewnia mocniejsze dopasowanie wzorców w wierszu poleceń.
Włączasz za pomocą shopt -s extglobi 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ą .club .hpliki, 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])
.club .hjest 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 extglobjest GLOBIGNOREzmienna . 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 GLOBIGNOREaby móc to zrobić rm *techno*w katalogu źródłowym.
Możesz także użyć dość prostej forpętli:
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
-maxdepth 1do nierekurencyjnych?
findw 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
bashmożna użyć while IFS='' read -r filename, ale wtedy nowe linie są nadal problemem. Zasadniczo najlepiej nie używać lsdo wyliczania plików; narzędzia takie findsą 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, findczy grepjest w leczeniu dwie listy plików jak zestawy i „diff” je za pomocą comm:
comm -23 <(ls) <(ls *Music*)
commjest lepszy niż, diffponieważ 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 lsi 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 *.txtplików w bieżącym katalogu, z wyjątkiem plików zaczynających się na liczbę.
To działa w bash, dash, zsha 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/*.txtspowoduje foriterację pętli po wszystkich plikach, /some/dirktó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 $FILEzamiast tego skrócić ).
W linii trzeciej wszystkie pliki pasujące do casewzorca [0-9]*zostaną pominięte ( continueinstrukcja przeskoczy do następnej iteracji forpę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]*|*.bakPominąć pliki oba .bakpliki oraz pliki, które nie zaczynają się od liczby.
*.txtzamiast 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
cppodręcznika w systemie MacOS ma -aopcję, ale robi coś zupełnie innego. Która platforma to obsługuje?
ls /dir/*/!(base*)