2009-04-26 8 views
46

Me he encontrado con un problema muy tonto con una secuencia de comandos de shell de Linux. Quiero eliminar todos los archivos con la extensión ".bz2" en un directorio. En el script llamo alrm no puede eliminar archivos por comodín de una secuencia de comandos, pero funciona desde un intérprete de comandos de la shell

rm "$archivedir/*.bz2" 

donde $ archivedir es una ruta de directorio. Debería ser bastante simple, ¿no? De alguna manera, se las arregla para fallar con este error:

rm: cannot remove `/var/archives/monthly/April/*.bz2': No such file or directory 

Pero hay es un archivo en ese directorio llamado test.bz2 y si cambio de secuencia de comandos para

echo rm "$archivedir/*.bz2" 

y copiar/pegar el salida de esa línea en una ventana de terminal el archivo se elimina con éxito. ¿Qué estoy haciendo mal?

+0

¿Cuál es la forma correcta de hacerlo si una variable contiene el comodín? Di 'archivedir ="/var/foo/*. Bz2 "'. – mandrake

Respuesta

87

TL; DR única

Cita la variable, no toda la trayectoria prevista con el comodín

rm "$archivedir"/*.bz2 

Explicación

  • En Unix, los programas generalmente no interpretan a sí mismos comodines. El intérprete de comandos interpreta comodines sin comillas y reemplaza cada argumento comodín con una lista de nombres de archivos coincidentes. si $ archivedir puede contener espacios, entonces rm $archivedir/*.bz2 no podría hacer lo que

  • Puede deshabilitar este proceso, indicando el carácter comodín, usando comillas simples o dobles, o una barra invertida antes de ella. Sin embargo, eso no es lo que quiere aquí; desea que el comodín se amplíe a la lista de archivos que coincida.

  • Tenga cuidado al escribir rm $archivedir/*.bz2 (sin las comillas). La palabra división (es decir, dividir la línea de comandos en argumentos) ocurre después de $ archivedir es sustituido. Entonces, si $ archivedir contiene espacios, entonces obtendrás argumentos adicionales que no intentabas. Diga archivedir es /var/archives/monthly/April to June. Luego obtendrás el equivalente de escribir rm /var/archives/monthly/April to June/*.bz2, que intenta eliminar los archivos "/ var/archives/monthly/April", "to" y todos los archivos que coincidan con "June/*. Bz2", que no es lo que querer.

La solución correcta es escribir:

rm "$archivedir"/*.bz2
+3

Wow, no tenía idea de que el intérprete de comandos realiza las operaciones comodín en lugar de los programas reales del sistema de archivos. ¡Esto es bueno saberlo! – chaz

+0

Explicación interesante, pero no explica por qué funciona cuando se ejecuta directamente en el prompt –

+1

La ejecución de "sudo rm/var/lib/OnlyRootCanList/*" no funcionará cuando el usuario actual no pueda enumerar los archivos en ese directorio. En tales casos, un comando de búsqueda funcionaría mejor. como 'encontrar. -name "* .jpg" | xargs rm ' –

4

Las comillas hacen que la cadena se interprete como una cadena literal, intente eliminarlas.

8

Solo para ampliar un poco esto, bash tiene reglas bastante complicadas para tratar los metacaracteres entre comillas. En general

  • casi nada se interpreta en comillas simples:

    echo '$foo/*.c'     => $foo/*.c 
    echo '\\*'      => \\* 
    
  • sustitución cáscara se hace dentro de las comillas dobles, pero metacaracteres de archivos no se expanden:

    FOO=hello; echo "$foo/*.c"  => hello/*.c 
    
  • todo lo que está dentro de las comillas inversas se pasa a la subshell que las interpreta. Una variable de shell que no se exporta no se define en la subshell. Por lo tanto, el primer comando se hace eco de blanco, pero la segunda y tercera "bye" eco:

    BAR=bye echo `echo $BAR` 
    BAR=bye; echo `echo $BAR` 
    export BAR=bye; echo `echo $BAR` 
    

(Y conseguir esto para imprimir la forma que desee en la toma varios intentos es aparentemente imposible. ..)

+0

Jonathan, eres un gran fanfarrón. ;-) Gracias. –

-1

Más información sobre el funcionamiento interno: intente escribir:

echo "$archivedir/*.bz2" 

en el intérprete de comandos. Verás que se expande de inmediato. Entonces, nunca veo el * en absoluto; en cambio, es esa lista que se le pasa.

Editar: Veo que básicamente lo intentó. Lo que quiere hacer entonces, es probarlo cuando hay varios archivos bz2 en ese directorio. Entonces verás el efecto.

7

Su línea original

rm "$archivedir/*.bz2" 

se puede volver a escribir como

rm "$archivedir"/*.bz2 

para lograr el mismo efecto. La expansión del comodín no se está llevando a cabo correctamente en su configuración existente. Al desplazar la comilla doble al "frente" de la ruta del archivo (que es legítimo) lo evita.

0

que he visto errores similares cuando se llama a un script de shell como ./shell_script.sh desde otro script. Esto se puede solucionar mediante la invocación como sh shell_script.sh

+0

Esto es solo programación de culto a la carga. No hay ninguna razón para que el shell incorrecto cause este error en particular. -1 – tripleee

0
¿Por qué no rm -rf */*.bz2

? Funciona para mí en OSX.

Cuestiones relacionadas