Respostas:
No Bash, você pode fazer isso ativando a extglob
opção, assim (substitua ls
por cp
e 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 extglob
opção shell oferece uma combinação de padrões mais poderosa na linha de comando.
Você liga shopt -s extglob
e 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 .c
ou .h
arquivos, 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])
.c
ou .h
ser 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 GLOBIGNORE
variá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 GLOBIGNORE
será possível rm *techno*
no diretório de origem.
Você também pode usar um for
loop bastante simples :
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
-maxdepth 1
para não recursivo?
find
de 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
bash
você pode usar while IFS='' read -r filename
, mas as novas linhas ainda são um problema. Em geral, é melhor não usar ls
para enumerar arquivos; ferramentas como find
sã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
, find
ou grep
é tratar duas listas de arquivos como conjuntos e "diff" -los usando comm
:
comm -23 <(ls) <(ls *Music*)
comm
é preferível ao diff
contrá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 ls
a 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 *.txt
arquivos no diretório atual, exceto aqueles que começam com um número.
Isso funciona em bash
, dash
, zsh
e 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/*.txt
fará com que o for
loop itere sobre todos os arquivos em /some/dir
cujo 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 case
padrão [0-9]*
) serão ignorados (a continue
instrução pula para a próxima iteração do for
loop). - 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]*|*.bak
pular arquivos de ambos os .bak
arquivos e arquivos que não começam com um número.
*.txt
vez 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
cp
página de manual no MacOS tem uma -a
opção, mas faz algo totalmente diferente. Qual plataforma suporta isso?
ls /dir/*/!(base*)