2011-01-18 31 views
5

(EDIT en solución con SED)Bash escapa y comodines tilde pero no el espacio

Tengo una lista de nombres de archivos y directorios, incluyendo tilde y comodines. Ej .:

~/docs/file.*  
~/my docs/*.txt 
... 

He leído las filas y pasarlos a un comando (por ejemplo, rsync):

while read ROW 
do 
    rsync $ROW /my/dest/ 
done < list.txt 

El problema está manejando con espacios en los nombres de archivo. Si pongo $ FILA entre comillas dobles como esto

rsync "$ROW" /my/dest/ 

por supuesto golpe no escapa a los comodines ni la tilde. Pero si no uso comillas, el espacio rompe la fila.

Una posible solución es cambiar IFS (cuidadosamente: el script es más complicado que el que informé). Otra solución (gracias por la solución a Patrick Echterbruch) es escaparse de los espacios. Sin embargo, el siguiente código no funciona para mí:

while read ROW 
do 
    export NEWROW=$(echo $ROW | sed -e 's/ /\\ /g') 
    echo "Value: $NEWROW" 
    ls -1d $NEWROW 
done < list.txt 

Tenga en cuenta que no se pasan las comillas a ls. existe el archivo "~/a b c/test.txt", pero me da:

Value: ~/saver/a\ b\ c/*.txt 
ls: impossibile accedere a ~/saver/a\: Nessun file o directory 
ls: impossibile accedere a b\: Nessun file o directory 
ls: impossibile accedere a c/*.txt: Nessun file o directory 

parece que newRow se pasa a ls como una cadena, por lo que los comodines no se expanden pero el espacio sigue rompiendo el nombre del archivo, aunque escapó.

¿Algún consejo? Gracias.

+0

Consulte [sobre la expansión de tilde] (http://mywiki.wooledge.org/BashPitfalls#echo_.22.2BAH4.22). –

Respuesta

1

Por lo que puedo ver, el script sed publicado no tendrá ningún efecto, las partes "buscar" y "reemplazar" son las mismas.

Lo que podría hacer podría hacer:

ROW=$(echo $ROW | sed -e "s/ /\\\\ /g") 

o (mejor):

ROW=$(echo $ROW | sed -e 's/ /\\ /g') 

ejemplo última utiliza comillas simples, por lo que la cáscara no trata la barra invertida especialmente.

En cuanto al segundo problema que surgió: Los caracteres especiales (como ~) se expanden por bash cuando se encuentran en los parámetros de un programa. No se expandirán cuando se almacenen en una variable, ni bash inspeccionarán y expandirán el contenido de una variable al pasarlo como parámetro a un comando.

Es por eso que rsync $NEWROW ... no funciona - rsync recibe el contenido no modificado de $ NEWROW como primer parámetro y luego tendría que realizar una expansión tipo shell por sí mismo.

La solución sólo he conocido de es ejecutar el comando en una subcapa, es decir:

ROW="~/a b c" 
$NEWROW=$(echo $ROW | sed -e 's/ /\\ /g') 
bash -c "ls -ld $f" 

Esto hará que el golpe se ejecuta actualmente para extraer el contenido de las variables (es decir ~/a\ b\ c y pasarla a la segunda cáscara está a punto de invocar. Este bash "interno" recibirá el comando junto con los parámetros y luego, por supuesto, a la expansión de los parámetros.

Perdón por perder este punto al principio, me centré solo en la parte de la expresión regular de la pregunta.

encontrado otra posible solución: Después de convertir el camino usando sed (por ejemplo NEWROW=$(echo $ROW | sed -e 's/ /\\ /g') intento:

eval ls -ld $NEWROW 

o eval rsync $ NewRow/otro/ruta

Esto debería funcionar, así, sin la actuación impacto de los subniveles empezando

// EDIT:. Añadido g que falta a la secuencia de comandos sed

// EDIT: Añadido solución para el problema de la expansión ruta

// EDIT: Addes solución eval

+0

¿Pudiste ejecutar esto en tu máquina local? No pude hacer que esto funcione. – chrisaycock

+0

Sí, funciona perfectamente bien. El comando: ROW = "my doc"; echo $ ROW; ROW = $ (echo $ ROW | sed-e 's// \\ /'); echo $ ROW devuelve dos líneas, una con un espacio puro, la otra con un espacio de barra invertida. sed --version: GNU sed versión 4.2 –

+1

Entonces nos falta la "g" final para reemplazar cada ocurrencia de espacio y no solo la primera ocurrencia: 's// \\/g'. Ahora me pregunto cómo omitir los espacios que ya se han escapado, pero creo que hay otras preguntas al respecto. – Narcolessico

0

creo que se puede canalizar la lista para ser rsync en un archivo separado, y ejecutarlo, como

while read ROW 
do 
    echo "rsync '$ROW' /my/dest/" 
done <list.txt> rsync.txt 

sh rsync.txt 
+0

Solución interesante, pero probablemente un poco lenta (especialmente si los comodines ampliados rinden a muchos archivos). – Narcolessico

Cuestiones relacionadas