Czy możesz wyjaśnić mi te trzy rzeczy w tym kodzie bash?

10

Mam functionw swoim .bashrcpliku. Wiem, co robi, zwiększa X wiele katalogówcd

Oto on:

up()
{
    local d=""
    limit=$1
    for ((i=1 ; i <= limit ; i++))
      do
        d=$d/..
      done
    d=$(echo $d | sed 's/^\///')
    if [ -z "$d" ]; then
      d=..
    fi
    cd $d
}

Ale czy możesz mi wyjaśnić te trzy rzeczy?

  1. d=$d/..
  2. sed 's/^\///'
  3. d=..

Dlaczego nie zrobić tego tak:

up()
{
    limit=$1

    for ((i=1 ; i <= limit ; i++))
    do
        cd ..
    done
}

Stosowanie:

<<<>>>~$ up 3
<<<>>>/$
coś tam coś tam
źródło

Odpowiedzi:

24
  1. d=$d/..dodaje /..do bieżącej zawartości dzmiennej. dzaczyna się pusty, potem robi to pierwsza iteracja /.., druga /../..itd.

  2. sed 's/^\///'upuszcza pierwszy /, więc /../..staje się  ../..(można to zrobić za pomocą rozszerzenia parametru d=${d#/}).

  3. d=.. ma sens tylko w kontekście jego stanu:

    if [ -z "$d" ]; then
      d=..
    fi

    Zapewnia to, że jeśli dw tym momencie jest pusty, przejdziesz do katalogu nadrzędnego. ( upbez argumentu jest równoważne cd ...)

To podejście jest lepsze niż iteracyjne, cd ..ponieważ zachowuje cd -- możliwość powrotu do poprzedniego katalogu (z perspektywy użytkownika) w jednym kroku.

Funkcję można uprościć:

up() {
  local d=..
  for ((i = 1; i < ${1:-1}; i++)); do d=$d/..; done
  cd $d
}

Zakłada się, że chcemy przejść o co najmniej jeden poziom i dodajemy poziomy n-1 , więc nie musimy usuwać wiodących /ani sprawdzać pustych $d.

Korzystanie z Athena jot( athena-jotpakiet w Debianie):

up() { cd $(jot -b .. -s / "${1:-1}"); }

(na podstawie wariantu sugerowanego przez glenn jackman ).

Stephen Kitt
źródło
Tak, $OLDPWDprzyszło mi do głowy , że zostałem zdeptany. I na Zsh z cdzestawem, aby użyć Dirstack, to też.
muru
Czy istnieje jakiś powód przypisywania $1do limitzamiast używania $1w pętli?
Nick Matteo
1
@kundor zapewnia domyślną wartość 0 (patrz arytmetyka powłoki ). ${1:-1}Zamiast tego moglibyśmy użyć , jak w wariancie Glenna.
Stephen Kitt
@kundor Również czytelność dla ludzi. Dobrze nazwana zmienna mówi ci, jakie jest zamierzone znaczenie lub cel pierwszego argumentu, gdy zaczynasz czytać funkcję, bez konieczności uprzedniego poznania, co robi funkcja lub zakończenia zrozumienia kodu, aby go wydedukować.
mtraceur
2

Ale czy możesz mi wyjaśnić te trzy rzeczy?

  1. d = $ d / ..

    To Łączy obecne zawartość var dz /..i przypisać go do d.
    Końcowym rezultatem jest utworzenie dpowtarzającego się łańcucha /../../../...

  2. sed 's / ^ ///'

    Usuń wiodące /z podanego ciągu d(echo $ d) w opublikowanym kodzie.
    Prawdopodobnie lepiej napisane, sed 's|^/||'aby uniknąć odwrotnego ukośnika.

    Alternatywą (szybszą i prostszą) jest pisanie d=${d#/}.

  3. d = ..

    Przypisz ciąg ..do var d.
    Ma to sens tylko jako sposób na upewnienie się, że dma co najmniej jeden .. w przypadku, gdy test if [ -z "$d" ]; thensygnalizuje, że var djest pusty. Może się to zdarzyć tylko dlatego, że polecenie sed usuwa jedną postać d.
    Jeśli nie ma potrzeby usuwania znaku z d, nie ma potrzeby używania sedlub if.


Kod w twoim pytaniu zawsze przesuwa się w górę o co najmniej jeden katalog.

Lepszy

  • local dwystarczy, aby upewnić się, że zmienna jest pusta, nic więcej nie jest potrzebne.
    Jednak lokalne działa tylko w niektórych powłokach, takich jak bash lub dash. W szczególności ksh(as yash) nie ma localpolecenia. Bardziej przenośnym rozwiązaniem jest:

    [ "$KSH_VERSION$YASH_VERSION" ] && typeset c='' d='' i='' || local c='' d='' i=''
  • for((Składnia nie jest przenośny. Lepiej użyj czegoś takiego:

    while [ "$((i+=1))" -lt "$limit" ]; do
  • Krzepki.
    Jeśli wartości podane w pierwszym argumencie funkcji mogą być ujemne lub tekstowe, funkcja powinna być odporna na przetwarzanie tych wartości.
    Po pierwsze, ograniczmy wartości tylko do liczb (użyję cdla count):

    c=${1%%[!0-9]*}

    A następnie ogranicz wartość zliczania tylko do wartości dodatnich:

    let "c = (c>0)?c:0"

Ta funkcja na szczęście akceptuje 0 lub dowolny tekst (bez błędów):

up()
{
    [ "$KSH_VERSION$YASH_VERSION" ] && typeset c='' d='' i='' || local c='' d='' i=''
    c=${1%%[!0-9]*}
    c=$(((c>0)?c:0))
    while [ "$((i+=1))" -le "$c" ]; do d="$d../"; done
    echo \
        cd "${d%/}"         # Removing the trailing / is not really needed for cd
                            # but it is nice to know it could be done cleanly.
}

up 2
up 0
up asdf

Usuń echo \po przetestowaniu funkcji.

Izaak
źródło