OSX Bash per loop: problemi con gli spazi nei nomi delle cartelle?

7

Sto diventando un po 'confuso su come gestire gli spazi nei nomi dei percorsi quando vengono restituiti in un ciclo for.

Razionale: sto pulendo le autorizzazioni su cartelle e file che copio da Windows. La maggior parte dei file finisce con -rwx------ o -rwxr-xr-x permessi quindi mi piace fare " chmod -x * " e poi " chmod u+x <folders> ", quindi sto provando il seguente:

$ alias getdirs='find . -maxdepth 1 -mindepth 1 -type d | cut -c 3-'
$ for i in $(getdirs); do chmod u+x $i; done

che funziona bene, purché le directory non abbiano uno spazio nel nome.

Ho provato diverse permutazioni di chmod u+x "$i" , chmod u+x '$i' e simili per ottenere il comportamento che volevo, ma senza risultato.

Come migliorare il mio codice bash, che funziona con i nomi delle cartelle contenenti spazio?

Lo scopo di questo è quello di essere in grado di rimuovere il bit "exec" dai file semplici (da cui la parte chmod -x * ) ma poi di ripristinarlo nelle directory per consentire di entrare in loro ( %codice%). Dai commenti e dalle risposte fino ad ora penso che probabilmente sarà più facile fare con l'incantesimo "trova" appropriato

    
posta JJarava 12.12.2013 - 13:25
fonte

5 risposte

10

Questo tipo di cose può essere complicato in tutte le shell Unix a causa del modo in cui lo spazio agisce come un separatore, l'esecuzione di alias come parte degli script di shell rende le cose ancora più interessanti. Probabilmente eseguirò due passaggi di find per impostare prima le directory in ordine, quindi i file seguenti:

find . -maxdepth 1 -mindepth 1 -type d -exec chmod u+x '{}' \;
find . -maxdepth 1 -mindepth 1 -type f -exec chmod u-x '{}' \;
    
risposta data 12.12.2013 - 14:07
fonte
6

In generale, il modo "corretto" di analizzare l'output di find in un loop bash consiste nell'utilizzare un ciclo while read , piuttosto che un ciclo for . In bash, per loop divisi per impostazione predefinita in spazi vuoti (spazio, tab, newline), questo può essere modificato, ma è più semplice e (a mio avviso) più pulito usare read , che legge una riga alla volta per impostazione predefinita .

find . -maxdepth 1 -mindepth 1 -type d | while read i; do chmod u+x "$i"; done

Si noti che ho citato il "$i" - ciò è altrettanto importante, perché la citazione delle variabili impedisce alla shell di dividere il loro contenuto (è lo stesso problema che ha for , ma dall'altra parte). Tieni inoltre presente che non puoi utilizzare le virgolette singole: '$i' restituirà un valore letterale $i , anziché il contenuto della variabile.

Ciò si interromperà ancora nelle directory con newline nei loro nomi. C'è una soluzione alternativa che coinvolge il% di co_de di find, ma ho sempre visto solo le nuove righe nei nomi di file creati appositamente per testare gli script. Non so se questo funziona con la versione di bash in OSX (tratta da wiki di greg ):

find . -maxdepth 1 -mindepth 1 -type d -print0 | while IFS= read -r -d '' i; do chmod u+x "$i"; done

Tuttavia, in questo caso è più facile usare i glob: un glob che termina con una -print0 si espanderà solo nelle directory, quindi potresti solo

chmod u+x */

(funzionerà perfettamente con spazi, newline, qualsiasi cosa). Più in generale, per scorrere tutte le directory:

for f in */; do stuff with "$f"; done

Sfortunatamente, non c'è modo di selezionare i file solo con glob in qualsiasi versione di bash (questo è uno dei motivi per cui preferisco zsh, dove i glob sono abbastanza potenti da non doversi preoccupare di trovare).

    
risposta data 12.12.2013 - 19:29
fonte
3

Ho due suggerimenti:

  1. Utilizza sed per mettere le virgolette attorno a tutti i nomi delle directory.
  2. Conduci a xargs con argomento -L 1 . Questo eseguirà un comando su ogni riga di stdin, evitando il ciclo for .

Prova questa pipeline:

find . -maxdepth 1 -mindepth 1 -type d | cut -c 3- | sed 's/.*/"&"/' | xargs -L 1 chmod u+x

    
risposta data 12.12.2013 - 15:54
fonte
2

Il nocciolo della questione è che la suddivisione in parole avviene per sostituzione dei comandi , in modo che i nomi con gli spazi in essi sono indistinguibili da nomi separati interamente. Globbing non soffre di questa difficoltà, quindi se c'è un modo per identificare le directory con un glob ed evitare il comando di sostituzione interamente, sei a casa libera. E c'è:

chmod -x *
chmod u+x */

Ma anche questo è troppo lavoro, perché chmod ha il flag simbolico X (maiuscola X), che applica il bit eseguibile solo a directory e file già eseguibili. Quindi puoi davvero solo fare:

chmod -x *
chmod u+X *
    
risposta data 12.12.2013 - 20:58
fonte
1

O il tuo alias sta solo tirando il primo bit prima dello spazio, o il tuo ciclo for sta solo leggendo il primo bit

Puoi testare questo aggiungendo qualche commento di prova nei tuoi comandi, ho limitato l'alias a solo tirare l'ultima directory trovata per isolare le iterazioni su 1, stampare ciò che l'alias pensa di ricevere, e quindi fare eco alla variabile contenuti per assicurarsi che corrispondano:

jaravj$ alias getdirs='find . -maxdepth 1 -mindepth 1 -type d | tail -1|  cut -c 3-'
jaravj$ getdirs
jaravj$ for i in $(getdirs); do echo "Filename is "$i; chmod u+x $i; done

EDIT: modificato do; echo ... in do echo ... poiché impediva l'esecuzione della riga

    
risposta data 12.12.2013 - 13:35
fonte

Leggi altre domande sui tag