Pouvez-vous m'expliquer ces trois choses dans ce code bash?

10

J'en ai un functiondans mon .bashrcdossier. Je sais ce qu'il fait, il augmente X de nombreux répertoires aveccd

C'est ici:

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
}

Mais pouvez-vous m'expliquer ces trois choses?

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

Pourquoi ne pas faire comme ça:

up()
{
    limit=$1

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

Usage:

<<<>>>~$ up 3
<<<>>>/$
quelque chose quelque chose
la source

Réponses:

24
  1. d=$d/..ajoute /..au contenu actuel de la dvariable. dcommence vide, puis la première itération le fait /.., la seconde /../..etc.

  2. sed 's/^\///'supprime le premier /, /../..devient  ainsi ../..(cela peut être fait en utilisant une extension de paramètre, d=${d#/}).

  3. d=.. n'a de sens que dans le contexte de son état:

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

    Cela garantit que, s'il dest vide à ce stade, vous accédez au répertoire parent. ( upsans argument équivaut à cd ...)

Cette approche est meilleure qu'itérative cd ..car elle préserve cd -- la possibilité de revenir au répertoire précédent (du point de vue de l'utilisateur) en une seule étape.

La fonction peut être simplifiée:

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

Cela suppose que nous voulons remonter d'au moins un niveau, et ajoute n - 1 niveaux, nous n'avons donc pas besoin de supprimer le début /ou de rechercher un vide $d.

Utiliser Athena jot(le athena-jotpaquet dans Debian):

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

(basé sur une variante suggérée par glenn jackman ).

Stephen Kitt
la source
Oui, $OLDPWDje me suis fait piétiner. Et sur zsh avec cdset pour utiliser la dirstack, ça aussi.
muru
Y a-t-il une raison pour attribuer $1à limitau lieu de simplement utiliser $1dans la boucle?
Nick Matteo
1
@kundor, il garantit que la valeur par défaut est 0 (voir l' arithmétique du shell ). Nous pourrions utiliser à la ${1:-1}place, comme dans la variante de glenn.
Stephen Kitt
@kundor Aussi la lisibilité humaine. Une variable bien nommée vous indique quelle est la signification ou le but prévu du premier argument lorsque vous commencez à lire la fonction, sans avoir à savoir d'abord ce que fait la fonction ou à finir de comprendre le code pour le déduire.
mtraceur
2

Mais pouvez-vous m'expliquer ces trois choses?

  1. d = $ d / ..

    Ceci concatène le contenu actuel de var davec /..et le réassigne d.
    Le résultat final est de créer dune chaîne répétée comme /../../../...

  2. sed 's / ^ ///'

    Supprimez l'interligne /de la chaîne fournie, d(echo $ d) dans le code que vous avez publié.
    Probablement mieux écrit sed 's|^/||'pour éviter la barre oblique inverse.

    Une alternative (plus rapide et plus simple) est d'écrire d=${d#/}.

  3. d = ..

    Attribuez la chaîne ..au var d.
    Cela n'a de sens que pour s'assurer qu'il en dexiste au moins un .. au cas où le test if [ -z "$d" ]; thensignale que la var dest vide. Et cela ne peut se produire que parce que la commande sed supprime un caractère de d.
    S'il n'est pas nécessaire de supprimer le caractère de d, il n'est pas nécessaire de sedou if.


Le code dans votre question remontera toujours d'au moins un dir.

Mieux

  • local dsuffit pour s'assurer que la variable est vide, rien d'autre n'est nécessaire.
    Cependant, local ne fonctionne que dans certains shells comme bash ou dash. En particulier, ksh(as yash) n'a pas de localcommande. Une solution plus portable est:

    [ "$KSH_VERSION$YASH_VERSION" ] && typeset c='' d='' i='' || local c='' d='' i=''
  • La for((syntaxe n'est pas portable. Mieux vaut utiliser quelque chose comme:

    while [ "$((i+=1))" -lt "$limit" ]; do
  • Robuste.
    Si les valeurs données au premier argument de la fonction peuvent être négatives ou textuelles, la fonction doit être robuste pour traiter ces valeurs.
    Tout d'abord, limitons les valeurs aux seuls nombres (je vais les utiliser cpour count):

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

    Et puis limitez la valeur de comptage uniquement à des valeurs positives:

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

Cette fonction acceptera rapidement 0 ou tout texte (sans erreur):

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

Retirez le echo \lorsque vous avez testé la fonction.

Isaac
la source