¿Puedes explicarme estas tres cosas en este código bash?

10

Tengo un functionen mi .bashrcarchivo. Sé lo que hace, intensifica X muchos directorios concd

Aquí está:

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
}

Pero, ¿puedes explicarme estas tres cosas?

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

¿Por qué no hacer así?

up()
{
    limit=$1

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

Uso:

<<<>>>~$ up 3
<<<>>>/$
algo algo
fuente

Respuestas:

24
  1. d=$d/..se agrega /..al contenido actual de la dvariable. dcomienza vacío, luego la primera iteración lo hace /.., la segunda, /../..etc.

  2. sed 's/^\///'cae el primero /, entonces se /../..convierte  ../..(esto se puede hacer usando un parámetro de expansión, d=${d#/}).

  3. d=.. solo tiene sentido en el contexto de su condición:

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

    Esto garantiza que, si destá vacío en este momento, vaya al directorio principal. ( upsin argumento es equivalente a cd ..).

Este enfoque es mejor que el iterativo cd ..porque conserva cd -la capacidad de volver al directorio anterior (desde la perspectiva del usuario) en un solo paso.

La función se puede simplificar:

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

Esto supone que queremos subir al menos un nivel, y agrega n - 1 niveles, por lo que no necesitamos eliminar el inicio /o verificar si hay un vacío $d.

Usando Athena jot(el athena-jotpaquete en Debian):

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

(basado en una variante sugerida por Glenn Jackman ).

Stephen Kitt
fuente
Sí, me $OLDPWDpisotearon. Y en zsh con cdset para usar el dirstack, eso también.
muru
¿Hay alguna razón para asignar $1en limitlugar de simplemente usar $1en el bucle?
Nick Matteo
1
@kundor asegura que el valor predeterminado es 0 (ver aritmética de shell ). Podríamos usar ${1:-1}en su lugar, como en la variante de Glenn.
Stephen Kitt
@kundor También legibilidad humana. Una variable bien nombrada le dice cuál es el significado o el propósito del primer argumento cuando comienza a leer la función, sin tener que saber primero qué hace la función o terminar de entender el código para deducirla.
mtraceur
2

Pero, ¿puedes explicarme estas tres cosas?

  1. d = $ d / ..

    Esto concatena el contenido actual de var dcon /..y lo asigna de nuevo a d.
    El resultado final es hacer duna cadena repetida como /../../../...

  2. sed 's / ^ ///'

    Elimine el inicio /de la cadena suministrada d(echo $ d) en el código que publicó.
    Probablemente mejor escrito sed 's|^/||'para evitar la barra invertida.

    Una alternativa (más rápida y simple) es escribir d=${d#/}.

  3. d = ..

    Asignar la cadena ..a la var d.
    Esto solo tiene sentido como una forma de garantizar que dtenga al menos uno .. en caso de que la prueba if [ -z "$d" ]; thenindique que la var destá vacía. Y eso solo puede suceder porque el comando sed está eliminando un personaje d.
    Si no hay necesidad de eliminar el carácter de d, no hay necesidad de sedo if.


El código en su pregunta siempre subirá al menos un directorio.

Mejor

  • local des suficiente para asegurarse de que la variable esté vacía, no se necesita nada más.
    Sin embargo, el local solo funciona en algunos shells como bash o dash. En específico, ksh(as yash) no tiene un localcomando. Una solución más portátil es:

    [ "$KSH_VERSION$YASH_VERSION" ] && typeset c='' d='' i='' || local c='' d='' i=''
  • La for((sintaxis no es portátil. Mejor use algo como:

    while [ "$((i+=1))" -lt "$limit" ]; do
  • Robusto.
    Si los valores dados al primer argumento de la función pueden ser negativos o de texto, la función debe ser robusta para procesar esos valores.
    Primero, limitemos los valores a solo números (lo usaré cpara count):

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

    Y luego limite el valor de conteo solo a valores positivos:

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

Esta función aceptará felizmente 0 o cualquier texto (sin errores):

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

Elimine el echo \cuando haya probado la función.

Isaac
fuente