2008-10-19 10 views

Respuesta

304

En Bash puede hacerlo activando la opción extglob, como esto (en lugar de ls para cp y añadir el directorio de destino, por supuesto)

~/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 

más tarde, puede desactivar extglob con

shopt -u extglob 
+8

Me gusta esta característica: 'ls/dir/* /! (Base *)' –

+6

¿Cómo se incluye todo (*) y también se excluye! (B *)? –

+2

¿Cómo coincidirías, por ejemplo, todo comenzando con 'f', excepto' foo'? – Noldorin

4

Una solución para esto se puede encontrar con find.

$ mkdir foo bar 
$ touch foo/a.txt foo/Music.txt 
$ find foo -type f ! -name '*Music*' -exec cp {} bar \; 
$ ls bar 
a.txt 

Encontrar tiene bastantes opciones, puede obtener bastante específico sobre lo que incluye y excluye.

Editar: Adam en los comentarios notó que esto es recursivo. encontrar opciones mindepth y maxdepth puede ser útil para controlar esto.

+0

Esto hace una copia recursiva, que es un comportamiento diferente. También genera un nuevo proceso para cada archivo, que puede ser muy ineficiente para una gran cantidad de archivos. –

+0

El costo de generar un proceso es aproximadamente cero en comparación con todo el IO generado por la copia de cada archivo. Así que diría que esto es lo suficientemente bueno para el uso ocasional. – dland

+0

Algunas soluciones para el proceso de desove: http://stackoverflow.com/questions/186099/how-do-you-handle-the-too-many-files-problem-when-working-in-bash –

18
No

en bash (que yo sepa), pero:

cp `ls | grep -v Music` /target_directory 

Sé que esto no es exactamente lo que estaba buscando, pero va a resolver su ejemplo.

+0

El ls predeterminado colocará varios archivos por línea, lo que probablemente no proporcione los resultados correctos. –

+8

Solo cuando stdout es una terminal. Cuando se utiliza en una tubería, l imprime un nombre de archivo por línea. –

+0

ls solo coloca varios archivos por línea si se envía a un terminal. Pruébelo usted mismo - "ls | less" nunca tendrá múltiples archivos por línea. – SpoonMeiser

5

También puede utilizar una bastante simple for bucle:

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

Esto hace un hallazgo recursivo, que es un comportamiento diferente de lo que desea OP. –

+1

usa '-maxdepth 1' para no recursivo? – avtomaton

+0

He encontrado que esta es la solución más limpia sin tener que habilitar/deshabilitar las opciones del shell. La opción -maxdepth se recomienda en esta publicación para obtener el resultado que necesita el OP, pero todo depende de lo que intente lograr. –

9

Si desea ev Identificación del costo de mem de usar el comando exec, creo que puede hacerlo mejor con xargs. Creo que la siguiente es una alternativa más eficiente a

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

La opción del shell extglob le da más poderosa coincidencia de patrones en la línea de comandos.

Lo enciende con shopt -s extglob, y lo apaga con shopt -u extglob.

En el ejemplo, tendría que inicialmente hacer:

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

El completo ext terminó pegote operadores de Bing son (extracto de man bash):

Si la opción del shell extglob se habilita utilizando el shopt incorporado, se reconocen varios operadores de coincidencia de patrones extendidos . En la siguiente descripción, un pat tern-list es una lista de uno o más patrones separados por un |. patrones compuestos se pueden formar usando uno o más de los siguientes sub-patrones:

  • ?(Patrón-lista)
    Partidos cero o una aparición de los patrones dados
  • * (patrón-lista)
    concuerda con cero o más apariciones de los patrones dados
  • + (lista-patrón)
    coincide con uno o más apariciones de los patrones dados
  • @ (patrón-lista)
    coincide con uno de los patrones dados
  • ! (Lista-patrón)
    concuerda con algo menos uno de los patrones dados

Así, por ejemplo, si desea una lista de todos los archivos en el directorio actual que no son .c o .h archivos, que harían:

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

Por supuesto, los trabajos normales cáscara globing, por lo que el último ejemplo también podría escribirse como:

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

¡Esto es realmente bueno! Voy a editar la pregunta un poco para que sea más general y espero que su respuesta pueda ser votada un poco más. – user4812

+4

nitpick: no son "expresiones regulares", son "patrones" de shell shell –

+1

@glenn: tienes toda la razón. – tzot

0

esto haría excluyendo exactamente 'Música'

cp -a ^'Music' /target 

esto y que la exclusión de cosas como la música? * O * Música

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

La página del manual 'cp' en MacOS tiene una opción' -a' pero hace algo completamente diferente. ¿Qué plataforma es compatible con esto? – tripleee

4

Mi preferencia personal es usar grep el comando while Esto le permite a uno escribir scripts potentes y legibles que le garanticen que terminará haciendo exactamente lo que desea. Además, al usar un comando de eco, puede realizar una ejecución en seco antes de llevar a cabo la operación real. Por ejemplo:

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

imprimirá los archivos que terminará copiando. Si la lista es correcta, el siguiente paso es simplemente reemplazar el comando echo con el comando copia de la siguiente manera:

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

Esto funcionará siempre que sus nombres de archivo no tengan pestañas, líneas nuevas, más de un espacio en una fila o barras diagonales inversas. Si bien estos son casos patológicos, es bueno estar al tanto de la posibilidad. En 'bash' puedes usar' while IFS = '' read -r filename', pero las nuevas líneas siguen siendo un problema. En general, es mejor no usar 'ls' para enumerar archivos; herramientas como 'encontrar' son mucho más adecuadas. – Thedward

+0

Sin herramientas adicionales: 'para archivo en *; do case $ {file} in (* Music *) ;; (*) cp "$ {file}"/target_directory; eco ;; esac; done' – Thedward

+0

http://mywiki.wooledge.org/ParsingLs enumera una serie de razones adicionales por las que debe evitar esto. – tripleee

2

las siguientes obras listas de todos los archivos *.txt en el directorio actual, a excepción de los que comienzan con un número.

Esto funciona en bash, dash, zsh y todas las demás shells compatibles con 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. En la línea uno del patrón /some/dir/*.txt hará que el bucle for para iterar sobre todos los archivos en /some/dir cuyo nombre termina con .txt.

  2. En la línea dos, se utiliza una declaración de caso para descartar los archivos no deseados. - La expresión ${FILE##*/} quita cualquier componente de nombre de directorio principal del nombre de archivo (aquí /some/dir/) para que los patrones solo puedan coincidir con el nombre base del archivo. (Si solo está descartando nombres de archivo basados ​​en sufijos, puede acortar esto a $FILE en su lugar.)

  3. En la tercera línea, todos los archivos que coinciden con el [0-9]*) case línea patrón se omitirán (la declaración continue salta a la siguiente iteración del bucle for). - Si lo desea, puede hacer algo más interesante aquí, p. como omitir todos los archivos que no comienzan con una letra (a-z) usando [!a-z]*, o puede usar varios patrones para omitir varios tipos de nombres de archivos, p. ej. [0-9]*|*.bak para omitir archivos .bak y archivos que no comienzan con un número.

+0

Esto no funciona (debian). ¿Lo has probado? – lauhub

+0

¡Doh! Hubo un error (coincidí con '* .txt' en lugar de' ''). Corregido ahora. – zrajm

3

En bash, una alternativa a shopt -s extglob es el GLOBIGNORE variable. No es realmente mejor, pero me resulta más fácil de recordar.

Un ejemplo que puede ser lo que el cartel original quería:

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

Cuando haya terminado, unset GLOBIGNORE para poder rm *techno* en el directorio de origen.

2

Un truco que no he visto aquí hasta ahora de que no utiliza extglob, find o grep es tratar a dos listas de archivos como conjuntos y "diff" usando comm:

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

comm es preferible sobre diff porque no tiene cruft extra.

Esto devuelve todos los elementos del conjunto 1, ls, que son no también en conjunto 2, ls *Music*. Esto requiere que ambos conjuntos estén en orden ordenado para que funcionen correctamente. No hay problema para ls y la expansión global, pero si está utilizando algo como find, asegúrese de invocar sort.

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

Potencialmente útil.

+1

Uno de los beneficios de la exclusión es no atravesar el directorio en primer lugar. Esta solución hace * dos * recorridos de subdirectorios, uno con la exclusión y otro sin ella. –

+0

Muy buen punto, @MarkStosberg. Aunque, un beneficio marginal de esta técnica es que puede leer exclusiones de un archivo real, p. 'comm -23 <(ls) exclude_these.list' –

Cuestiones relacionadas