2016-05-14 12 views
5

Piszę funkcję bash, aby uzyskać wszystkie repozytoria git, ale napotkałem problem, gdy chcę przechowywać wszystkie ścieżki do repozytorium git do tablicy patharray. Oto kod:Dołącz do zmiennej tablicowej z polecenia potoku

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[@]} 
} 

Chcę zapisać wszystkie ścieżki repozytorium do tablicy patharray, ale nie mogę go poza pipeline który składa się z polecenia locate i while.
Ale mogę uzyskać tablicę w komendzie pipeline, komenda komentarza # echo -e ${patharray[@]} działa dobrze, jeśli jest nieakomentowana, więc jak mogę rozwiązać problem?

Próbowałem polecenia export, jednak wydaje się, że nie może przekazać patharray do potoku.

+0

'echo -e' będzie również rozszerzyć backslashy w ścieżkach (nie, że są one bardzo prawdopodobne, aby być tam ...). –

+0

Dzięki za przypomnienie, lepszym sposobem jest użycie 'printf'. – zhenguoli

Odpowiedz

2

Przede wszystkim dołączanie do zmiennej tablicowej jest lepiej wykonana z array[${#array[*]}]="value" lub array+=("value1" "value2" "etc"), chyba że chcesz przekształcić całą tablicę (której nie chcesz).

Teraz, od pipeline commands are run in subprocesses, zmiany wprowadzone w zmiennej wewnątrz polecenia rurociągu nie będą propagowane na zewnątrz.Istnieje kilka opcji, aby ominąć to (w większości są wymienione w Greg's BashFAQ/024):

  • przekazać wynik poprzez standardowe wyjście zamiast

    • najprostsze; musisz zrobić to w każdym razie, aby uzyskać wartość z funkcji (chociaż there are ways to return a proper variable)
    • żadnych znaków specjalnych ścieżek mogą być obsługiwane za pomocą niezawodnie \0 jako separator (patrz Capturing output of find . -print0 into a bash array do czytania \0 -separated list)

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

      lub po prostu

      locate -b0 '\.git' | xargs -0 dirname -z 
      
  • av oid uruchomiony pętlę w podproces

    • uniknąć rurociąg w ogóle

      • tymczasowego pliku/FIFO (bad: wymaga ręcznego czyszczenia, dostępną dla innych)
      • zmienną tymczasową (mierny: niepotrzebnego pamięci narzut)
      • podstawienie procesu (specjalny, obsługiwany przez składnię przypadek FIFO, nie wymaga ręcznego czyszczenia; Kod adaptacją 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') 
        
    • należy wybrać opcję lastpipe (nowość w Bash 4.2) - nie działa ostatni polecenie rurociągu w podproces (mierny: ma wpływ globalnego)

+0

Wreszcie, 'xargs' w połączeniu z' -0'. Świetna odpowiedź. –

4

Bash wykonuje wszystkie polecenia potoku w oddzielnych SubShell s. Kiedy kończy się podpowłoką zawierającą pętlę while, wszystkie zmiany dokonane w zmiennej zostaną utracone.

Można po prostu grupa pętla while oraz oświadczenie echo razem więc oba są zawarte w tej samej podpowłoce:

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 
} 

Alternatywnie, można zorganizować swój kod, aby nie trzeba rurę: use ProcessSubstitution (zobacz także instrukcję Bash szczegóły - 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 
} 
Powiązane problemy