Comment puis-je passer au répertoire frère précédent / suivant?

10

J'ai souvent une disposition de répertoire de projet comme celle-ci

project
`-- component-a
|   `-- files...
`-- component-b
|   `-- files...
`-- component-c
    `-- files...

Je travaille généralement dans l'un des componentrépertoires, car c'est là que se trouvent les fichiers. Lorsque je reviens ensuite au shell, je dois souvent simplement passer à un répertoire frère, en particulier lorsque j'ai besoin d'apporter quelques modifications non scriptables à chaque composant. Dans ces cas, je ne me soucierai même pas du répertoire frère précédent sur lequel je vais travailler, ou du répertoire frère suivant.

Puis-je définir une commande prevou nextqui va simplement cdme dans le répertoire précédent, ou le répertoire suivant (par alphabet ou autre)? Parce que taper cd ../com<TAB><Arrow keys>tout le temps devient un peu vieux.

Esteis
la source

Réponses:

8

N'utilisez pas la solution commandlinefu de l'autre réponse : c'est dangereux¹ ET inefficace.² Au lieu de cela, si vous utilisez bash, utilisez simplement les fonctions suivantes. Pour les rendre persistants, mettez-les dans votre .bashrc. Notez que j'utilise la commande globale car elle est intégrée et facile. Cependant, l'ordre global est généralement alphabétique dans la plupart des pays. Vous obtiendrez un message d'erreur s'il n'y a pas de répertoire suivant ou précédent dans lequel aller. En particulier, vous verrez l'erreur si vous essayez de nextou prevalors que dans le répertoire racine, /.

## bash and zsh only!
# functions to cd to the next or previous sibling directory, in glob order

prev () {
    # default to current directory if no previous
    local prevdir="./"
    local cwd=${PWD##*/}
    if [[ -z $cwd ]]; then
        # $PWD must be /
        echo 'No previous directory.' >&2
        return 1
    fi
    for x in ../*/; do
        if [[ ${x#../} == ${cwd}/ ]]; then
            # found cwd
            if [[ $prevdir == ./ ]]; then
                echo 'No previous directory.' >&2
                return 1
            fi
            cd "$prevdir"
            return
        fi
        if [[ -d $x ]]; then
            prevdir=$x
        fi
    done
    # Should never get here.
    echo 'Directory not changed.' >&2
    return 1
}

next () {
    local foundcwd=
    local cwd=${PWD##*/}
    if [[ -z $cwd ]]; then
        # $PWD must be /
        echo 'No next directory.' >&2
        return 1
    fi
    for x in ../*/; do
        if [[ -n $foundcwd ]]; then
            if [[ -d $x ]]; then
                cd "$x"
                return
            fi
        elif [[ ${x#../} == ${cwd}/ ]]; then
            foundcwd=1
        fi
    done
    echo 'No next directory.' >&2
    return 1
}

¹ Il ne gère pas tous les noms de répertoires possibles. L'analyse de la lssortie n'est jamais sûre .

² cdn'a probablement pas besoin d'être terriblement efficace, mais 6 processus est un peu excessif.

jw013
la source
1
J'utilise zsh, en fait, mais si vous modifiez le premier test dans la boucle for de la fonction suivante, [[ -n $foundcwd ]]votre réponse fonctionne également sous bash et zsh. Très bien, et merci d'avoir écrit ceci.
Esteis
4

La fonction suivante permet de passer aux répertoires frères (fonction bash)

function sib() {
    ## sib  search sibling directories 
    ##   prompt for choice (when two or more directories are found, current dir is removed from choices) 
    ##   change to directory after selection 
    local substr=$1
    local curdir=$(pwd)
    local choices=$(find .. -maxdepth 1 -type d -name "*${substr}*" | grep -vE '^..$' | sed -e 's:../::' | grep -vE "^${curdir##*/}$" | sort)
    if [ -z "$choices" ]; then
        echo "Sibling directory not found!"
        return
    fi
    local count=$(echo "$choices" | wc -l)
    if [[ $count -eq 1 ]]; then
        cd ../$choices
        return 
    fi
    select dir in $choices; do
        if [ -n "$dir" ]; then
            cd ../$dir
        fi
        break
    done
}

Un exemple d'utilisation:

$ tree
  .
  ├── component-aaa-01
  ├── component-aaa-02
  ├── component-bbb-01
  ├── component-bbb-02
  ├── component-ccc-01
  ├── component-ccc-02
  └── component-ccc-03
  7 directories, 0 files
  $ cd component-aaa-01/
  $ sib bbb-01
  $ pwd
  component-bbb-01
  $ sib bbb
  $ pwd
  component-bbb-02
  $ sib ccc
  1) component-ccc-01
  2) component-ccc-02
  3) component-ccc-03
  #? 3
  $ pwd
  component-ccc-03
  $ sib 01
  1) component-aaa-01
  2) component-bbb-01
  3) component-ccc-01
  #? 2
  $ pwd
  component-bbb-01
kitekat75
la source
Très agréable. Je garde la réponse de jw013 comme la réponse acceptée, parce que ma question et mon cas d'utilisation étaient «Je veux passer au prochain frère et je me fiche de son nom»; mais cela est utile aussi, et astucieux, alors ayez un vote positif.
Esteis
2

J'ai trouvé une astuce sur commandlinefu.com . Je le republie ici pour le rendre plus trouvable, avec une explication et une nextcommande ajoutées pendant que j'y suis.

alias prev='cd ../"$(ls -F .. | grep '/' | grep -B1 -xF "${PWD##*/}/" | head -n 1)"'
alias next='cd ../"$(ls -F .. | grep '/' | grep -A1 -xF "${PWD##*/}/" | tail -n 1)"'

La magie est dans le bloc `$ (...). Il envoie quelques commandes les unes dans les autres comme suit:

ls -F .. |   # list items in parent dir; `-F` requests filetype indicators
grep '/' |   # select the directories (written as  `mydir/`)
grep -B1 -xF "${PWD##*/}/" |   # search for the name of the current directory in the output;
                               # print it and the line preceding it
head -n 1    # the first of those two lines contains the name of the previous sibling
Esteis
la source
2
La commande que vous avez trouvée présente un certain nombre de problèmes. Je l'ai édité pour corriger les plus flagrants: il traitait le nom du répertoire comme un modèle grep et lui permettait de correspondre à une sous-chaîne; et il avait le nom du répertoire se dilatent shell (séparation de mots et englobement) deux fois (toujours utiliser des guillemets doubles autour de substitutions de variables et de commandes: "$foo", "$(foo)"). En outre, l' analyse de la sortie de lsn'est pas fiable , elle peut échouer avec des noms de fichiers contenant des caractères non imprimables.
Gilles 'SO- arrête d'être méchant'