2016-05-14 18 views
5

Sto scrivendo una funzione bash per ottenere tutti i repository git, ma ho incontrato un problema quando voglio archiviare tutti i nomi di percorso del repository git nell'array patharray. Ecco il codice:Aggiungi a una variabile di matrice da un comando pipeline

gitrepo() { 
    local opt 

    declare -a patharray 
    locate -b '\.git' | \ 
     while read pathname 
     do 
      pathname="$(dirname ${pathname})" 
      if [[ "${pathname}" != *.* ]]; then 
      # Note: how to add an element to an existing Bash Array 
       patharray=("${patharray[@]}" '\n' "${pathname}") 
       # echo -e ${patharray[@]} 
      fi 
     done 
    echo -e ${patharray[@]} 
} 

Io voglio salvare tutti i percorsi di repository alla matrice patharray, ma io non riesco a farlo al di fuori del pipeline che si compone di comando locate e while.
Ma posso ottenere la matrice nel comando pipeline, il comando commentato # echo -e ${patharray[@]} funziona bene se non commentato, quindi come posso risolvere il problema?

E ho provato il comando export, tuttavia sembra che non possa passare il patharray alla pipeline.

+0

'eco -e' sarà anche ampliare backslash nei percorsi (non che sono molto suscettibili di essere lì ...). –

+0

Grazie per avermelo ricordato, il modo migliore è usare 'printf'. – zhenguoli

risposta

2

Prima di tutto, aggiungendo ad una variabile di matrice è migliore fatto con array[${#array[*]}]="value" o array+=("value1" "value2" "etc") a meno che non si desideri trasformare l'intero array (cosa che non si fa).

Ora, dal pipeline commands are run in subprocesses, le modifiche apportate a una variabile all'interno di un comando pipeline non si propagheranno all'esterno di esso.Ci sono alcune opzioni per aggirare questo (la maggior parte sono elencati in Greg's BashFAQ/024):

  • passare il risultato attraverso stdout invece

    • la più semplice; avrete bisogno di farlo comunque per ottenere il valore dalla funzione (anche se there are ways to return a proper variable)
    • caratteri speciali nei percorsi possono essere gestite in modo affidabile utilizzando \0 come separatore (vedi Capturing output of find . -print0 into a bash array per la lettura \0 liste -separated)

      locate -b0 '\.git' | while read -r -d '' pathname; do dirname -z "$pathname"; done 
      

      o semplicemente

      locate -b0 '\.git' | xargs -0 dirname -z 
      
  • av OID che esegue il ciclo in un sottoprocesso

    • evitare di tubazione in tutti i file temporanei

      • /FIFO (cattivo: richiede la pulizia manuale, accessibile ad altri)
      • variabile temporanea (mediocre: la memoria non necessaria overhead)
      • sostituzione del processo (uno speciale caso FIFO supportato dalla sintassi, non richiede la pulizia manuale; Codice adattato da Greg's BashFAQ/020):

        i=0 #`unset i` will error on `i' usage if the `nounset` option is set 
        while IFS= read -r -d $'\0' file; do 
            patharray[i++]="$(dirname "$file")" # or however you want to process each file 
        done < <(locate -b0 '\.git') 
        
    • utilizzare l'opzione lastpipe (nuova in Bash 4.2) - non viene eseguito l'ultimo comando didi una condotta in un sottoprocesso (mediocre: ha un effetto globale)

+0

Infine, 'xargs' combinato con' -0'. Bella risposta. –

4

Bash esegue tutti i comandi di una pipeline in separati SubShell s. Quando termina una sottoshell contenente un ciclo while, tutte le modifiche apportate alla variabile patharray vengono perse.

Si può semplicemente gruppo il ciclo while e la echo dichiarazione insieme in modo che siano entrambi contenuti all'interno della stessa subshell:

gitrepo() { 
    local pathname dir 
    local -a patharray 

    locate -b '\.git' | {      # the grouping begins here 
     while read pathname; do 
      pathname=$(dirname "$pathname") 
      if [[ "$pathname" != *.* ]]; then 
       patharray+=("$pathname")  # add the element to the array 
      fi 
     done 
     printf "%s\n" "${patharray[@]}"  # all those quotes are needed 
    }           # the grouping ends here 
} 

In alternativa, è possibile strutturare il codice per non avere bisogno di un tubo: utilizzare ProcessSubstitution (vedi anche il manuale di Bash per i dettagli - man bash | less +/Process\ Substitution):

gitrepo() { 
    local pathname dir 
    local -a patharray 

    while read pathname; do 
     pathname=$(dirname "$pathname") 
     if [[ "$pathname" != *.* ]]; then 
      patharray+=("$pathname")  # add the element to the array 
     fi 
    done < <(locate -b '\.git') 

    printf "%s\n" "${patharray[@]}"  # all those quotes are needed 
}