Rurociągi dla wyjścia pętli zapobiegają modyfikacji zmiennych lokalnych

11

Próbuję napisać prostą funkcję bash, która jako argumenty przyjmuje wiele plików i / lub katalogów. Powinno:

  1. W pełni kwalifikuj nazwy plików.
  2. Sortuj je.
  3. Usuń duplikaty.
  4. Wydrukuj wszystko, co faktycznie istnieje.
  5. Zwraca liczbę nieistniejących plików.

Mam skrypt, który prawie robi to, co chcę, ale spada na sortowanie. Zwracana wartość skryptu w jego obecnej postaci jest poprawna, ale dane wyjściowe nie są (nieposortowane i duplikaty). Jeśli odkomentuję | sort -uinstrukcję, jak wskazano, wynik jest poprawny, ale wartość zwracana jest zawsze 0.

Uwaga: łatwiejsze rozwiązania problemu są mile widziane, ale tak naprawdę pytanie dotyczy tego, dlaczego tak się dzieje w kodzie, który mam. To znaczy, dlaczego dodanie potoku pozornie zatrzymuje skrypt zwiększający zmienną r?

Oto skrypt:

function uniqfile
{
    local r=0 

    for arg in "$@"
    do  
        readlink -e "$arg" || (( ++r ))

    done #| sort -u    ## remove that comment

    return $r
}
tjm
źródło
Tylko mała obserwacja. Możesz zredukować for arg in "$@"do for arg. „If 'in WORDS ...;” nie jest obecny, wówczas zakłada się „w” $ @ ”.” - pomoc dla
manatwork

Odpowiedzi:

15

Jest to dobrze znany pułapkę z powodu tej funkcji :

Każde polecenie w potoku jest wykonywane jako osobny proces (tj. W podpowłoce).

tak, aby zmodyfikowane zmienne były lokalne dla podpowłoki i nie były widoczne ponownie w rodzicu.

Aby tego uniknąć, przepisz swój kod, aby uniknąć potoku, z podstawieniem procesu:

 for arg in "$@"
    do  
        readlink -e "$arg" || (( ++r ))

    done > >(sort -u)
enzotib
źródło
Dziękuję Ci. To wspaniale. Zastanawiam się, czy możesz mi powiedzieć nazwę >(..command..)konstruktu. Myślę , że wiem, jak to działa, ale uważam, że powinienem trochę poczytać.
tjm 30.09.11
2
@tjm: nazywa się to substytucją procesu
enzotib
Podstawianie procesów w Bash ma wiele form: tldp.org/LDP/abs/html/process-sub.html
slm
Podstawianie procesów jest formą komunikacji międzyprocesowej, która pozwala, aby wejście lub wyjście polecenia pojawiło się jako plik. Polecenie jest zastępowane w wierszu, w którym normalnie występuje nazwa pliku , przez powłokę polecenia. Pozwala to programom, które zwykle akceptują tylko pliki, bezpośrednio odczytywać lub zapisywać w innym programie.
nobar
3

Te | sort -usiły bit poprzedzający (więc cała pętla for), aby uruchomić w sub-process (bash potrzebuje „” STDOUT przekierować do sort„standardowego wejścia”. (Internet wydaje się myśleć kshi bashobsługiwać tę sprawę nieco inaczej .. pierwszy lub ostatni polecenie w sekwencji potoku zostaje umieszczone w podpowłoce?)

Wątek dotyczy podobnego problemu i na końcu ma fajne rozwiązanie: http://ubuntuforums.org/showthread.php?t=312017

fragment
    #!/bin/bash
    exec 3< <(du | sort -n)  

    n=0
    while read size dir; do
      [ $size -gt 1000 ] && ((n++))
    done <&3
    exec 3<&-

    echo "Found $n too big files"
PT
źródło