Respostas:
No Bash, você pode fazer isso ativando a extglobopção, assim (substitua lspor cpe adicione o diretório de destino, é claro)
~/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
Mais tarde, você pode desativar o extglob com
shopt -u extglob
f, exceto foo?
A extglobopção shell oferece uma combinação de padrões mais poderosa na linha de comando.
Você liga shopt -s extglobe desliga com shopt -u extglob.
No seu exemplo, você faria inicialmente:
$ shopt -s extglob
$ cp !(*Music*) /target_directory
O completo disponível ext terminou glob operadores bing são (excerto man bash):
Se a opção extglob shell estiver ativada usando o shopt interno, vários operadores estendidos de correspondência de padrões serão reconhecidos. Uma lista de padrões é uma lista de um ou mais padrões separados por um |. Padrões compostos podem ser formados usando um ou mais dos seguintes subpadrões:
- ? (lista de padrões)
Corresponde a zero ou uma ocorrência dos padrões fornecidos- * (lista de padrões)
Corresponde a zero ou mais ocorrências dos padrões fornecidos- + (lista de padrões)
Corresponde a uma ou mais ocorrências dos padrões fornecidos- @ (lista
de padrões ) Corresponde a um dos padrões fornecidos- ! (lista de padrões)
Corresponde a qualquer coisa, exceto um dos padrões fornecidos
Portanto, por exemplo, se você quiser listar todos os arquivos no diretório atual que não são .cou .harquivos, faça:
$ ls -d !(*@(.c|.h))
Obviamente, o globing normal de shell funciona, então o último exemplo também pode ser escrito como:
$ ls -d !(*.[ch])
.cou .hser um diretório.
D, mas não o conteúdo dos subdiretórios que podem estar contidos no diretório D.
Não no bash (que eu saiba), mas:
cp `ls | grep -v Music` /target_directory
Eu sei que isso não é exatamente o que você estava procurando, mas resolverá o seu exemplo.
Se você quiser evitar o custo de mem do uso do comando exec, acredito que você pode fazer melhor com o xargs. Eu acho que o seguinte é uma alternativa mais eficiente para
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/
Em festa, uma alternativa para shopt -s extglobé a GLOBIGNOREvariável . Não é realmente melhor, mas acho mais fácil lembrar.
Um exemplo que pode ser o que o pôster original queria:
GLOBIGNORE="*techno*"; cp *Music* /only_good_music/
Quando terminar, unset GLOBIGNOREserá possível rm *techno*no diretório de origem.
Você também pode usar um forloop bastante simples :
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
-maxdepth 1para não recursivo?
findde backticks quebrará de maneira desagradável se encontrar algum nome de arquivo não trivial.
Minha preferência pessoal é usar o grep e o comando while. Isso permite escrever scripts poderosos e legíveis, garantindo que você acabe fazendo exatamente o que deseja. Além disso, usando um comando de eco, você pode executar uma execução a seco antes de executar a operação real. Por exemplo:
ls | grep -v "Music" | while read filename
do
echo $filename
done
imprimirá os arquivos que você acabará copiando. Se a lista estiver correta, a próxima etapa é simplesmente substituir o comando echo pelo comando copy da seguinte maneira:
ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done
bashvocê pode usar while IFS='' read -r filename, mas as novas linhas ainda são um problema. Em geral, é melhor não usar lspara enumerar arquivos; ferramentas como findsão muito mais adequadas.
for file in *; do case ${file} in (*Music*) ;; (*) cp "${file}" /target_directory ; echo ;; esac; done
Um truque que eu não vi aqui ainda que não usa extglob, findou grepé tratar duas listas de arquivos como conjuntos e "diff" -los usando comm:
comm -23 <(ls) <(ls *Music*)
commé preferível ao diffcontrário, porque não possui crosta extra.
Isso retorna todos os elementos do conjunto 1 ls, que também não estão no conjunto 2 ls *Music*,. Isso requer que os dois conjuntos estejam na ordem classificada para funcionar corretamente. Não há problema para lsa expansão glob, mas se você estiver usando algo como find, não se esqueça de invocar sort.
comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)
Potencialmente útil.
comm -23 <(ls) exclude_these.list
Uma solução para isso pode ser encontrada com o find.
$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt
O Find tem algumas opções, você pode ser bem específico sobre o que incluir e excluir.
Edit: Adam nos comentários observou que isso é recursivo. As opções de localização mindepth e maxdepth podem ser úteis para controlar isso.
find -maxdepth 1 -not -name '*Music*'/ target_directory
O trabalho a seguir lista todos os *.txtarquivos no diretório atual, exceto aqueles que começam com um número.
Isso funciona em bash, dash, zshe todos os outros shells compatíveis com 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
Na linha um, o padrão /some/dir/*.txtfará com que o forloop itere sobre todos os arquivos em /some/dircujo nome termina .txt.
Na linha dois, uma declaração de caso é usada para eliminar arquivos indesejados. - A ${FILE##*/}expressão retira qualquer componente principal do nome do diretório do nome do arquivo (aqui /some/dir/) para que os padrões possam corresponder apenas ao nome da base do arquivo. (Se você estiver eliminando apenas os nomes de arquivos com base em sufixos, poderá abreviá-los $FILE.)
Na linha três, todos os arquivos correspondentes ao casepadrão [0-9]*) serão ignorados (a continueinstrução pula para a próxima iteração do forloop). - Se você quiser, pode fazer algo mais interessante aqui, por exemplo, como pular todos os arquivos que não começam com uma letra (a – z) usando [!a-z]*, ou você pode usar vários padrões para pular vários tipos de nomes de arquivos, por exemplo, [0-9]*|*.bakpular arquivos de ambos os .bakarquivos e arquivos que não começam com um número.
*.txtvez de apenas *). Corrigido agora.
isso faria excluindo exatamente 'Música'
cp -a ^'Music' /target
isso e aquilo para excluir coisas como Music? * ou *? Music
cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target
cppágina de manual no MacOS tem uma -aopção, mas faz algo totalmente diferente. Qual plataforma suporta isso?
ls /dir/*/!(base*)