¿Hay una línea que me permita crear un directorio y moverme al mismo tiempo?

150

Me encuentro repitiendo mucho de:

mkdir longtitleproject
cd longtitleproject

¿Hay alguna manera de hacerlo en una línea sin repetir el nombre del directorio? Estoy de fiesta aquí.

método de acción
fuente
2
Relacionado (pero más general): ejecuta dos comandos en un argumento (sin secuencias de comandos) .
G-Man
1
mkdir longtitleprojectentoncescd !^
user262826
También se ha cubierto en askubuntu.com/q/927463/295286 y askubuntu.com/q/249314/295286
Sergiy Kolodyazhnyy

Respuestas:

160

No hay un comando incorporado, pero puede escribir fácilmente una función que llame a mkdircontinuación cd:

mkcd () {
  mkdir "$1"
  cd "$1"
}

Ponga este código en su ~/.bashrcarchivo (o ~/.kshrcpara usuarios de ksh, o ~/.zshrcpara usuarios de zsh). Define una función llamada mkcd. "$1"será reemplazado por el argumento de la función cuando la ejecutes.

Esta versión simple tiene varios defectos:

  • No puede crear una cadena de subdirectorios a la vez. Solución: pasa la -popción a mkdir. (Esto puede o no ser deseable, ya que aumenta el riesgo de que un error tipográfico pase desapercibido, por ejemplo, mkcd mydierctory/newsubse creará felizmente mydierctoryy mydierctory/newsubcuando se pretende crear newsubdentro de lo existente mydirectory).
  • Si el argumento comienza con -, pero no es justo -, entonces mkdiry cdlo interpretará como una opción. Si es justo -, entonces cdlo interpretará en el sentido $OLDPWD. Si es +seguido por 0 o más dígitos, cden zsh lo interpretará como un índice en la pila de directorios. Puede solucionar el primer problema, pero no los otros dos, pasando --antes del argumento. Puede solucionar todos estos problemas anteponiendo ./el argumento si es una ruta relativa.
  • mkdirno sigue CDPATH, pero cdsí, así que si ha establecido CDPATHun valor que no comienza con .(una configuración ciertamente inusual), cdpuede llevarlo a un directorio diferente al que acaba de crear. Anteponer ./rutas relativas soluciona esto (hace CDPATHque se ignore).
  • Si mkdirfalla, intenta ejecutarse cd. Solución: se usa &&para separar los dos comandos.

Todavía bastante simple:

mkcd () {
  case "$1" in /*) :;; *) set -- "./$1";; esac
  mkdir -p "$1" && cd "$1"
}

Esta versión todavía tiene el potencial de hacer que cdvaya a un directorio diferente del que mkdiracaba de crear en un caso extremo: si el argumento mkcdcontiene ..y pasa a través de un enlace simbólico. Por ejemplo, si el directorio actual es /tmp/herey mylinkes un enlace simbólico /somewhere/else, mkdir mylink/../foocrea /somewhere/else/foomientras que cd mylink/../foocambia a foo. No es suficiente buscar enlaces simbólicos en el argumento, porque el shell también rastrea enlaces simbólicos en su propio directorio actual, por lo cd /tmp/mylink; mkdir ../foo; cd ../fooque no cambia al nuevo directorio ( /somewhere/else/foo) sino a /tmp/foo. Una solución para esto es dejar que el cdincorporado resuelva todos los ..componentes de la ruta primero (no tiene sentido usar foo/..sifoono existe, por lo que mkdirnunca necesita ver ninguno ..).

Llegamos a esta versión robusta aunque un poco sangrienta:

mkcd () {
  case "$1" in
    */..|*/../) cd -- "$1";; # that doesn't make any sense unless the directory already exists
    /*/../*) (cd "${1%/../*}/.." && mkdir -p "./${1##*/../}") && cd -- "$1";;
    /*) mkdir -p "$1" && cd "$1";;
    */../*) (cd "./${1%/../*}/.." && mkdir -p "./${1##*/../}") && cd "./$1";;
    ../*) (cd .. && mkdir -p "${1#.}") && cd "$1";;
    *) mkdir -p "./$1" && cd "./$1";;
  esac
}

(Ejercicio: ¿por qué estoy usando un subshell para la primera cdllamada?)

Si mkdir falla, quiero asegurarme de no cambiar el directorio actual. Cambiar de nuevo con cd o $ OLDPWD no es lo suficientemente bueno si el shell no tiene permiso para cambiar a su directorio actual. Además, llamar a cd actualiza OLDPWD, por lo que solo queremos hacerlo una vez (o restaurar OLDPWD).


También hay formas menos especializadas de no tener que volver a escribir la palabra de la línea anterior:

  • Escriba cd , luego Esc .(o Alt+ .) para insertar el último argumento del comando anterior.
  • cd !$se ejecuta cden el último argumento del comando anterior.
  • Presione Uppara recuperar la línea de comandos anterior, y luego editarlo para cambiar mkdira cd.
Gilles
fuente
¡Gracias! la esc. me parece lo más conveniente, ¿tiene la secuencia de teclas algún significado especial?
methodofaction
Es solo la secuencia Bash (y heredada de ksh, y también funciona zsh) para "repetir la última palabra del comando anterior". Lo uso con bastante frecuencia.
geekosaur
31
@Gilles Estoy empezando a pensar que la cuenta "Gilles" en realidad es compartida por un panel de expertos. ;-)
Keith
@StephaneChazelas /a/b/..//realmente funcionaría pero no /a/b/../c. Fijo. He planteado la pregunta a un público más amplio.
Gilles
1
mkcd() { mkdir -p "$1" && cd "$1"; }no parece ser un problema en (mi instancia de) zsh. mkdir -p /var/tmp/somewhere/else /tmp/here; cd /tmp/here; ln -s /var/tmp/somewhere/else mylink; mkdir -p mylink/../foo && cd mylink/../foo; pwd(incluye la configuración y) muestra /tmp/here/foocuál es lo que se creó (y lo que esperaba). basherróneamente crea y cambia a /var/tmp/somewhere/foo.
Adam Katz
134

Esta es la línea que necesita. No se necesita otra configuración:

mkdir longtitleproject && cd $_

La $_variable, en bash, es el último argumento dado al comando anterior. En este caso, el nombre del directorio que acaba de crear. Como se explica en man bash:

_         At  shell  startup,  set to the absolute pathname used to invoke
          the shell or shell script being executed as passed in the  envi
          ronment  or  argument  list.   Subsequently, expands to the last
          argument to the previous command, after expansion.  Also set  to
          the  full  pathname  used  to  invoke  each command executed and
          placed in the environment exported to that command.  When check
          ing  mail,  this  parameter holds the name of the mail file cur
          rently being checked."$_" is the last argument of the previous command.

Use cd $_para recuperar el último argumento del comando anterior en lugar de cd !$porque cd !$da el último argumento del comando anterior en el historial del shell :

cd ~/
mkdir folder && cd !$

terminas en casa (o ~ /)

cd ~/
mkdir newfolder && cd $_

terminas en una nueva carpeta debajo de casa !! (o ~ / nueva carpeta)

Jesús Carrera
fuente
23
¿Por qué en la Tierra ésta no es la respuesta aceptada
JSmyth
1
@JSmyth Estoy de acuerdo, se trata de una sola línea que utiliza la funcionalidad nativa cáscara
sming
Creo que el OP está tratando de evitar el uso de los dos comandos. Esta respuesta es (casi) tan válida como hacerlo mkdir foo && cd foo, lo cual no es útil.
josemigallas
77
El OP está pidiendo un one-liner que no requiera repetir el nombre del directorio, y esto es todo
Jesús Carrera
Esta es la línea tradicional a la que te refieres o has visto a otros usar en documentos / tutoriales. En realidad, hay 3 respuestas perfectas aquí. Este, el de @ jordan-harris, y la respuesta seleccionada. Depende de su configuración y preferencia.
Wade
30

Nunca se me habría ocurrido escribir este comportamiento porque escribo lo siguiente casi cada hora ...

$ mkdir someDirectory<ENTER>
$ cd !$

donde bash amablemente sustituye !$con la última palabra de la última línea; es decir, el nombre de directorio largo que ingresó.

Además, la finalización del nombre de archivo es su amigo en tales situaciones. Si su nuevo directorio fuera el único archivo en la carpeta, un doble rápido TABle daría el nuevo directorio sin tener que volver a ingresarlo.

Aunque es genial que bash le permita crear secuencias de comandos de tareas tan comunes como sugieren las otras respuestas, creo que es mejor aprender las características de edición de línea de comandos que bash tiene para ofrecer para que cuando trabaje en otra máquina no se pierda la sintaxis azúcar que proporcionan sus scripts personalizados.

Robar
fuente
1
¿Hay un buen lugar para conocer las peculiaridades más importantes de bash como esta?
dominicbri7
@ dominicbri7 Generalmente viene bajo "edición de línea de comando bash" Googlear lo mismo da lo siguiente como resultado superior web.mit.edu/gnu/doc/html/features_7.html Más específicamente ,! $ es un ejemplo de un designador de Word ver gnu. org / software / bash / manual / bashref.html # Word-Designators
Rob
17

Según ¿Qué personalizaciones ha realizado en su perfil de shell para aumentar la productividad? así es como lo hago:

# make a directory and cd to it
mcd()
{
    test -d "$1" || mkdir "$1" && cd "$1"
}

significa que también funciona si el directorio ya existe.

Mikel
fuente
44
La -popción de mkdir suprimirá los errores.
Glenn Jackman
1
mcd es un comando ya existente. Aunque acaba de dar un ejemplo, lo usé yo mismo, ya que es una carta más corta que mkcd.
Dharmit
@Dharmit Shah: ¿Cuál es el mcdcomando existente ? ¿Qué paquete o proyecto proporciona este comando?
Mikel
2
mtools proporciona el comando mcd. Su página de manual dice "El comando mcd se usa para cambiar el directorio de trabajo de mtools en el disco de MS-DOS".
Dharmit
13

Si usa Oh My Zsh, hay un comando llamado take que hace exactamente esto. Se vería algo así.

take myfolder

En realidad encontré este por accidente. Acabo de mirar y aparece en este truco del wiki Oh My Zsh GitHub. Es un comando bastante útil, y aparentemente muy fácil de crear.

Jordan Harris
fuente
1
Nunca supe sobre take:) ¡Perfecto! por cierto - iTerm2 con Oh My Zsh. En realidad, hay 3 respuestas perfectas aquí. Este, el de @ jesús-carrera, y la respuesta seleccionada. Depende de su configuración y preferencia.
Wade
3

O simplemente podría crear una variable corta sobre la marcha y usarla dos veces x = longproject ; mkdir $x ; cd $x, lo que admito es aún más largo que usar una función shellscript :)

sakisk
fuente
0

Aquí hay una ligera variante que vale la pena mencionar:

function mkdir() {
    local dir=$1
    command mkdir "$dir"  &&  cd "$dir"
}

Agregue esto a su ~/.bash_profiley luego puede usarlo mkdirnormalmente (una vez que lo haya sourcehecho), excepto que ahora ejecutará la función anterior en lugar del mkdircomando estándar .

Tenga en cuenta que esto no valida la entrada según la respuesta aceptada por Gilles , sino que demuestra cómo puede (efectivamente) anular las incorporaciones.

De los documentos (parafraseando ligeramente):

command mkdir [args]corre mkdircon argsignorando cualquier función de shell llamado mkdir. Solo se ejecutan los comandos incorporados de shell o los comandos encontrados al buscar la RUTA. Si hay una función de shell llamada ls, la ejecución command lsdentro de la función ejecutará el comando externo en lslugar de llamar a la función de forma recursiva

Creo que builtinlogra un resultado similar a command.

Arj
fuente
definitivamente debes citar$dir
Jeff Schaller
@Jeff, estuvo de acuerdo, pero la respuesta aceptada tiene toda la validación que uno necesitaría. Solo estoy presentando el uso de commandcomo alternativa.
Arj
0

Agregar función auxiliar a BASH, ZSH o KSH

Crea mkcdcomandos para tu entorno en una línea

echo -e 'mkcd() {\n mkdir -p "$1" && cd $_\n}' >> ~/.${0//-/}rc && . ~/.${0//-/}rc
Del mes próximo
fuente
-1

Simplemente automaticé las respuestas anteriores e hice un script ejecutable único:

fun='
mkcd ()
{
    mkdir -p -- "$1" && cd -P -- "$1"
}'

echo "$fun" >> ~/.bashrc

Simplemente copie esto en un nuevo archivo mkcd.shy ejecútelo solo una vez en la terminal bash mkcd.sh. Luego, ejecute source ~/.bashrcpara que funcione en la sesión actual.

Después de esto, puede usar mkcd some_dirpara crear e ingresar directamente en ese directorio.

subtleseeker
fuente
¿Sugiere escribir un guión (en un archivo) cuyo único propósito es agregar al ~/.bashrcarchivo (con una respuesta que ya se ha dado)? ¿Y cómo sugieres crear este mkcd.shscript? ¿Con un editor, tal vez? Esto parece más trabajo que solo editar ~/.bashrc. ¿Qué ventaja tiene esto sobre la respuesta aceptada? ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... PD: Esto fallará debido a problemas de citas, lo que me dice que ni siquiera lo ha intentado usted mismo.
Scott
Lamento decirlo, pero sí, como escribí en mi respuesta, usé las respuestas anteriores. Funciona Si no me crees, pruébalo. Y sobre no intentarlo, utilicé mi día completo para escribir tales scripts en bash y python hoy.
subtleseeker
Intenté lo que escribiste. No , no funciona.
Scott