¿Cómo puedo cd al directorio de hermanos anterior / siguiente?

10

A menudo tengo un diseño de directorio de proyecto como este

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

Por lo general, estaré trabajando en uno de los componentdirectorios, porque ahí es donde están los archivos. Cuando vuelvo al shell, a menudo simplemente necesito moverme a un directorio hermano, especialmente cuando necesito hacer algunos cambios no programables en cada componente. En esos casos, ni siquiera me importará cuál es el directorio de hermanos anterior en el que trabajaré, o el siguiente directorio de hermanos.

¿Puedo definir un comando prevo nextque simplemente cdme llevará al directorio anterior o al siguiente directorio (por alfabeto o lo que sea)? Porque escribir cd ../com<TAB><Arrow keys>todo el tiempo se está volviendo un poco viejo.

Esteis
fuente

Respuestas:

8

No use la solución commandlinefu de la otra respuesta : es inseguro¹ E ineficiente.² En cambio, si está usando bash, simplemente use las siguientes funciones. Para hacerlos persistentes, póngalos en su .bashrc. Tenga en cuenta que uso el orden global porque está integrado y es fácil. Por lo general, el orden global es alfabético en la mayoría de las configuraciones regionales. Recibirá un mensaje de error si no hay un directorio siguiente o anterior al que ir. En particular, verá el error si se intenta nexto prevmientras está en el directorio raíz, /.

## 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
}

¹ No maneja todos los nombres de directorio posibles. La lssalida de análisis nunca es segura .

² cdprobablemente no necesita ser terriblemente eficiente, pero 6 procesos es un poco excesivo.

jw013
fuente
1
En realidad, uso zsh, pero si cambias la primera prueba en el bucle for de la siguiente función, [[ -n $foundcwd ]]entonces tu respuesta funciona en bash y zsh igualmente bien. Muy bien, y gracias por escribir esto.
Esteis
4

La siguiente función permite cambiar a directorios hermanos (función 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 ejemplo de uso:

$ 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
fuente
Muy agradable. Mantengo la respuesta de jw013 como la aceptada, porque mi pregunta y caso de uso fueron 'Quiero pasar al próximo hermano, y no me importa cómo se llame'; pero esto también es útil e ingenioso, así que ten un voto a favor.
Esteis
2

Encontré un consejo en commandlinefu.com . Lo vuelvo a publicar aquí para que sea más fácil de encontrar, con una explicación y un nextcomando agregado mientras estoy en ello.

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 magia está en el bloque `$ (...). Canaliza algunos comandos entre sí de la siguiente manera:

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
fuente
2
El comando que encontró tiene varios problemas. Lo he editado para corregir los más atroces: estaba tratando el nombre del directorio como un patrón grep y permitía que coincidiera con una subcadena; y tenía el nombre del directorio se someten a la expansión del forro (división de palabras y comodines) dos veces (siempre utilizar comillas dobles y variables de comando: sustituciones "$foo", "$(foo)"). Además, el análisis de la salida de lsno es confiable , puede fallar con nombres de archivo que contienen caracteres no imprimibles.
Gilles 'SO- deja de ser malvado'