2008-10-19 11 views

Odpowiedz

304

w bash można to zrobić poprzez włączenie opcji extglob, tak (zastąpienia LS 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żna później wyłączyć extglob z

shopt -u extglob 
+8

Podoba mi się ta funkcja: 'ls/dir/* /! (Base *)' –

+6

Jak uwzględnić wszystko (*), a także wykluczyć! (B *)? –

+2

Jak pasowałoby, powiedzmy, wszystko zaczynające się od 'f', z wyjątkiem' foo'? – Noldorin

4

Jednym z rozwiązań można znaleźć za pomocą polecenia znajdowania.

$ 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ć bardzo szczegółowe informacje o tym, co dodajesz i wykluczasz.

Edytuj: Adam w komentarzach zauważył, że jest to rekursywne. znaleźć opcje mindepth i maxdepth mogą być przydatne w kontrolowaniu tego.

+0

Wykonuje kopię rekursywną, co jest innym zachowaniem. Tworzy również nowy proces dla każdego pliku, który może być bardzo nieefektywny w przypadku dużej liczby plików. –

+0

Koszt tarowania procesu wynosi w przybliżeniu zero w porównaniu do wszystkich zamówień, które kopiuje każdy plik. Powiedziałbym, że jest to wystarczająco dobre do sporadycznego użycia. – dland

+0

Niektóre sposoby obejścia procesu: http://stackoverflow.com/questions/186099/how-do-y-handle-the-too-many-files-problem-when-working-in-bash –

18

Nie w bash (które znam), ale:

cp `ls | grep -v Music` /target_directory 

wiem, że to nie jest dokładnie to, czego szukaliśmy, ale będzie to rozwiązać swój przykład.

+0

Domyślnie ls umieści wiele plików w wierszu, co prawdopodobnie nie przyniesie właściwych rezultatów. –

+8

Tylko wtedy, gdy standardowe wyjście jest terminalem. W przypadku użycia w potoku ls drukuje po jednej nazwie w linii. –

+0

ls tylko umieszcza wiele plików w linii, jeśli wysyłane są do terminala. Spróbuj sam - "ls | less" nigdy nie będzie mieć wielu plików w linii. – SpoonMeiser

5

Można także użyć dość prosty for pętlę:

for f in `find . -not -name "*Music*"` 
do 
    cp $f /target/dir 
done 
+1

Powoduje to rekurencyjne znalezisko, które różni się od tego, co chce OP. –

+1

użyć '-maxdepth 1' dla nierekursywnego? – avtomaton

+0

Znalazłem to najczystsze rozwiązanie bez konieczności włączania/wyłączania opcji powłoki. Opcja -maxdepth byłaby zalecana w tym poście, aby uzyskać wynik potrzebny OP, ale wszystko zależy od tego, co próbujesz osiągnąć. –

9

Jeśli chcesz AVO id koszt mem korzystania z polecenia exec, wierzę, że można lepiej z xargs. Myślę, że po to bardziej wydajna alternatywa 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/ 
186

Opcja powłoki extglob daje mocniejsze dopasowanie wzorca w linii poleceń.

Włączasz go, używając shopt -s extglob, i wyłącza się, używając shopt -u extglob.

W przykładzie, należy najpierw zrobić:

$ shopt -s extglob 
$ cp !(*Music*) /target_directory 

Pełne dostępny ext zakończył glob operatorzy Bing są (fragment man bash):

Jeśli opcja extglob shell jest włączona przy użyciu wbudowanego shopt, kilka rozszerzonych rozpoznających wzorce operatorów jest rozpoznawanych. W poniższym opisie tern-lista jest listą jednego lub więcej wzorów oddzielonych znakiem |. Kompozytowe stanie może być utworzony przy użyciu jednej lub więcej z następujących pod-wzorów:

  • ?(Wzór z listy)
    zero lub jedno wystąpienie podanych wzorach
  • * (wzór z listy)
    zero lub więcej wystąpień określonych wzorów
  • + (wzór z listy)
    obejmujący jedno lub więcej zdarzeń z podanych wzorów
  • @ (wzór z listy)
    zgodny z jednym z podanych wzorów
  • ! (Wzorzec-lista)
    zestawienia niczego z wyjątkiem jednego z podanych wzorów

Tak więc, na przykład, jeśli chcesz wyświetlić wszystkie pliki w bieżącym katalogu, które nie są .c lub .h pliki, byś zrobił:

$ ls -d !(*@(.c|.h)) 

oczywiście normalne prace powłoki globing, więc ostatni przykład można także zapisać jako:

$ ls -d !(*.[ch]) 
+0

To jest naprawdę dobre! Zmienię trochę to pytanie, aby uczynić je bardziej ogólnym i mam nadzieję, że twoja odpowiedź może uzyskać więcej głosów. – user4812

+4

Nitpick: nie są to "wyrazy regularne", to nazwy ścieżek "wzorce" –

+1

@glenn: masz absolutną rację. – tzot

0

to zrobi to bez dokładnie 'Music'

cp -a ^'Music' /target 

to i to za wyjątkiem rzeczy jak muzyka? * Lub *? Muzyka

cp -a ^\*?'complete' /target 
cp -a ^'complete'?\* /target 
+0

Strona podręcznika 'cp' na MacOS ma opcję' -a', ale robi coś zupełnie innego. Która platforma obsługuje to? – tripleee

4

Moje osobiste preferencje jest użycie grep i Polecenie while. Pozwala to na pisanie potężnych, a jednocześnie czytelnych skryptów, dzięki którym w końcu robisz dokładnie to, co chcesz. Dodatkowo za pomocą komendy echo możesz wykonać jazdę na sucho przed wykonaniem rzeczywistej operacji. Na przykład:

ls | grep -v "Music" | while read filename 
do 
echo $filename 
done 

wydrukuje pliki, które zostaną skopiowane. Jeżeli lista jest poprawna następnym krokiem jest po prostu zastąpić polecenia echo z polecenia kopiowania następująco:

ls | grep -v "Music" | while read filename 
do 
cp "$filename" /target_directory 
done 
+1

To będzie działać tak długo, jak długo twoje nazwy plików nie będą zawierały żadnych kart, znaków nowej linii, więcej niż jednego spacji z rzędu ani żadnych ukośników odwrotnych. Chociaż są to przypadki patologiczne, dobrze jest być świadomym tej możliwości. W 'bash' możesz użyć' while IFS = '' read -r filename', ale wtedy newlines nadal są problemem. Ogólnie najlepiej nie używać polecenia "ls" do wyliczania plików; narzędzia takie jak 'find' są znacznie lepiej dopasowane. – Thedward

+0

Bez żadnych dodatkowych narzędzi: 'dla pliku w *; do case $ {plik} w (* Muzyka *) ;; (*) cp "$ {plik}"/target_directory; Echo ;; esac; done' – Thedward

+0

http://mywiki.wooledge.org/ParsingLs podaje szereg dodatkowych powodów, dla których powinieneś tego unikać. – tripleee

2

następujące prace list wszystkie *.txt pliki w bieżącym katalogu, z wyjątkiem tych, które zaczynają się od cyfry.

To działa w bash, dash, zsh i wszystkich innych zgodnych powłokach 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 
  1. Zgodnie jednego wzorca /some/dir/*.txt spowoduje pętla for iteracyjne nad wszystkie pliki w /some/dir którego nazwa kończy się .txt.

  2. W linii drugiej instrukcja przypadku służy do usuwania niepożądanych plików. - Wyrażenie ${FILE##*/} usuwa pasujący składnik nazwy dir z nazwy pliku (tutaj /some/dir/), tak aby wzorce mogły się dopasować tylko do nazwy podstawowej pliku. (Jeśli usuwasz nazwy plików tylko na podstawie przyrostków, możesz je skrócić do $FILE.)

  3. Zgodnie trzy, wszystkie pliki pasujące do case wzór [0-9]*) Linia zostanie pominięty (oświadczenie continue przeskakuje do następnej iteracji pętli for). - Jeśli chcesz, możesz tutaj zrobić coś ciekawszego, np. jak pominięcie wszystkich plików, które nie zaczynają się od litery (a-z) przy użyciu [!a-z]*, lub można użyć wielu wzorców do pominięcia kilku rodzajów nazw plików, np. [0-9]*|*.bak, aby pominąć pliki zarówno .bak, jak i pliki, które nie zaczynają się od numeru.

+0

To nie działa (debian). Testowałeś to? – lauhub

+0

Doh! Wystąpił błąd (pasowałem do '* .txt' zamiast po prostu' * '). Naprawiono teraz. – zrajm

3

W bashu alternatywą shopt -s extglob jest GLOBIGNORE variable. To naprawdę nie jest lepsze, ale łatwiej mi je zapamiętać.

Przykładem, który może być co oryginalny plakat chciał:

GLOBIGNORE="*techno*"; cp *Music* /only_good_music/ 

Po zakończeniu unset GLOBIGNORE móc rm *techno* w katalogu źródłowym.

2

Sztuczka Nie widziałem tu jeszcze, że nie używa extglob, find lub grep jest traktowanie dwie listy plików w zestawach i „trudno” je za pomocą comm:

comm -23 <(ls) <(ls *Music*) 

comm jest preferowany w stosunku do diff, ponieważ nie ma dodatkowego dryfu.

Powoduje zwrócenie wszystkich elementów zestawu 1, ls, które są nie również w zestawie 2, ls *Music*. Wymaga to uporządkowania obu zestawów, aby działały poprawnie. Nie ma problemu z ls i globalnym rozszerzeniem, ale jeśli używasz czegoś takiego jak find, pamiętaj, aby wywołać sort.

comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort) 

Potencjalnie użyteczny.

+1

Jedną z korzyści wykluczenia nie jest przemierzanie katalogu w pierwszej kolejności. To rozwiązanie wykonuje * dwa * przechodzenie pod-katalogów - jedno z wykluczeniem i jedno bez. –

+0

Bardzo dobry punkt, @MarkStosberg. Chociaż jedną z zalet tej techniki jest możliwość odczytu wyjątków z rzeczywistego pliku, np. 'comm -23 <(ls) exclude_these.list' –

Powiązane problemy