2011-01-18 14 views
5

(EDIT in soluzione con sed)Bash sfugge e caratteri jolly tilde, ma non lo spazio

Ho una lista di nomi di file e directory, tra cui tilde e caratteri jolly. Es .:

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

Ho letto le righe e passare ad un comando (ad esempio rsync):

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

Il problema sta gestendo con spazi di nomi. Se metto $ ROW tra virgolette come questo

rsync "$ROW" /my/dest/ 

naturalmente bash non sfugge i caratteri jolly né la tilde. Ma se non uso le virgolette, lo spazio spezza la riga.

Una possibile soluzione è la modifica IFS (attentamente: lo script è più complicato di quello che ho segnalato). Un'altra soluzione (grazie per la correzione a Patrick Echterbruch) è quella di evitare gli spazi. Tuttavia, il seguente codice non funziona per me:

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

Si prega di notare che non vengono passate citazioni a ls. Il file "~/a b c/test.txt" esiste, ma ottengo:

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 

Sembra come newRow è passato a ls come una stringa, quindi i caratteri jolly non sono espansi, ma lo spazio mantiene rompere il nome del file, però sfuggito.

Qualche consiglio? Grazie.

+0

Si prega di vedere [questo riguardo all'espansione del tilde] (http://mywiki.wooledge.org/BashPitfalls#echo_.22.2BAH4.22). –

risposta

1

Per quanto posso vedere, lo script sed che hai pubblicato non avrà alcun effetto - le parti "search" e "replace" sono le stesse.

cosa si potrebbe fare potrebbe fare:

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

o (meglio):

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

esempio secondo usa virgolette singole, in modo che la shell non tratta il backslash appositamente.

Riguardo al secondo problema emerso: I caratteri speciali (come ~) vengono espansi da bash quando vengono rilevati nei parametri di un programma. Non verranno espansi quando li si memorizza in una variabile, né bash ispezionerà ed espanderà il contenuto di una variabile quando lo passerà come parametro a un comando.

Questo è il motivo per cui rsync $NEWROW ... non funziona: rsync riceve il contenuto non modificato di $ NEWROW come primo parametro e dovrebbe quindi eseguire un'espansione simile a una shell.

L'unica soluzione ho conosciuto fra è quello di eseguire il comando in una subshell, cioè:

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

questo modo il bash in esecuzione per estrarre i contenuti variabili (cioè ~/a\ b\ c e passarlo al secondo guscio sta per invocare. Questa bash "interiore" riceverà il comando insieme ai parametri e quindi ovviamente all'espansione dei parametri.

Ci scusiamo per la mancanza di questo punto all'inizio, mi sono concentrato solo sulla parte regex della domanda.

trovato un'altra soluzione possibile: Dopo la conversione il percorso utilizzando sed (ad es NEWROW=$(echo $ROW | sed -e 's/ /\\ /g') prova:

eval ls -ld $NEWROW 

o eval rsync $ newRow/altro/percorso

Questo dovrebbe funzionare così, senza la prestazione impatto della sottoshell partire

// EDIT:. Aggiunto mancante g allo script sed

// EDIT: Aggiunta una soluzione per il problema di espansione di percorso

// EDIT: Addes soluzione eval

+0

Sei riuscito a farlo sul tuo computer locale? Non ho potuto farlo funzionare. – chrisaycock

+0

Sì, funziona perfettamente. Il comando: ROW = "my doc"; echo $ ROW; ROW = $ (echo $ ROW | sed -e 's// \\ /'); echo $ ROW restituisce due righe, una con uno spazio puro, l'altra con uno spazio con escape alla barra rovesciata. sed --version: GNU sed versione 4.2 –

+1

Quindi ci manca la "g" finale per sostituire ogni occorrenza di spazio e non solo la prima occorrenza: 's// \\/g'. Ora mi chiedo come saltare gli spazi già sfuggiti, ma penso che ci siano altre domande a riguardo. – Narcolessico

0

penso che è possibile reindirizzare l'elenco per rsync in un file separato, ed eseguirlo, come

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

sh rsync.txt 
+0

Soluzione interessante, ma probabilmente un po 'lenta (specialmente se i caratteri jolly espansi portano a molti file). – Narcolessico

Problemi correlati