¿Por qué no funciona "cd" en un script de shell?


767

Estoy tratando de escribir un pequeño script para cambiar el directorio actual a mi directorio de proyecto:

#!/bin/bash
cd /home/tree/projects/java

Guardé este archivo como proyecto, agregué permiso de ejecución chmody lo copié /usr/bin. Cuando lo llamo por:, projno hace nada. ¿Qué estoy haciendo mal?


10
cruz duplicado sitio: superuser.com/questions/176783/...
Lesmana

En el futuro, siempre puede intentar probarlo pwden la última línea. Así que antes de secuencia de finalización a continuación, se puede comprobar que está funcionando o no ..
sobi3ch

55
@lesmana, ¿cómo es eso un duplicado?
aland

Debido @aland OP hace no en el hecho de ejecutar el script, es por eso que el directorio de trabajo no cambia para él. cdEl comando funciona bien dentro de los scripts, pruébelo usted mismo.
Alexander Gonchiy

Respuestas:


635

Los scripts de shell se ejecutan dentro de un subshell, y cada subshell tiene su propio concepto de cuál es el directorio actual. Lo cdlogra, pero tan pronto como la subshell sale, estás de vuelta en la shell interactiva y nada ha cambiado allí.

Una forma de evitar esto es usar un alias en su lugar:

alias proj="cd /home/tree/projects/java"

77
Los alias no son tan flexibles para administrar o cambiar. En el caso de muchos 'cd's, los scripts podrían ser mejores.
Thevs

16
Las funciones son más flexibles que los alias, por lo que es el siguiente aspecto cuando los alias no son suficientes.
Ephemient

44
¿Vale la pena señalar que en MS-DOS, el comportamiento de los scripts fue que un script llamado podría cambiar el directorio (e incluso la unidad) del shell del comando de llamada? ¿Y que Unix no tiene este defecto?
Jonathan Leffler el

23
Jonathan: si bien es cierto, en realidad no está relacionado con la pregunta. ¡Las respuestas sobre SO obtendrían el doble de tiempo si tuvieran que enumerar cada una de las deficiencias correspondientes en MS-DOS!
Greg Hewgill el

17
Otra forma de evitarlo es obtener el archivo de script: . my-scripto source my-script.
HelloGoodbye

505

¡No estás haciendo nada malo! Ha cambiado el directorio, pero solo dentro de la subshell que ejecuta el script.

Puede ejecutar el script en su proceso actual con el comando "punto":

. proj

Pero preferiría la sugerencia de Greg de usar un alias en este caso simple.


95
.también se deletrea source, elija el que le parezca más memorable.
Ephemient

47
@Ephemient: Buena PointSource Eso explica por qué workssource también demuestra que la pereza no-necesidad-es a menudo la madre de inventionsource
Adam Liss

8
@ephemient: tenga en cuenta que la fuente se utiliza en C shell y Bash; no se admite en shells POSIX o Korn, ni en el shell Bourne clásico.
Jonathan Leffler

2
"fuente" se utiliza en Z-shell
Nathan Moos

1
@AdamLiss Funciona muy bien, pero hay un inconveniente: de alguna manera el comando source / dot deshabilita la posibilidad de completar automáticamente el nombre del script / comando con la tecla TAB. Todavía es posible agregar argumentos / entrada con la tecla TAB, pero desafortunadamente el nombre del comando debe ingresarse directamente. ¿Sabes cómo hacer que la tecla TAB funcione en este caso?
Rafal

215

los cd en el script de vista técnico trabajaron , ya que cambió el directorio de la cáscara que corría el guión, pero eso fue un proceso separado en forma de horquilla de su consola interactiva.

Una forma compatible con Posix para resolver este problema es definir un procedimiento de shell en lugar de un script de comando invocado por shell .

jhome () {
  cd /home/tree/projects/java
}

Simplemente puede escribir esto o ponerlo en uno de los diversos archivos de inicio de shell.


66
Estoy de acuerdo, esta es la mejor respuesta, topado. Además, el alias puede ser apropiado en algunas situaciones, pero si está tratando de usar cd en un script y desea agregar algo más, un alias sería inútil.
Justin Buser

44
¡Esto parece una solución! Y estoy tratando de implementarlo, pero no funciona :( aquí está mi script: #! / Bin / bash jhome () {echo "please" cd / home echo "work"} jhome
Gant Laborde

1
@GantMan, debe agregar esto en un "archivo de inicio de shell", como ~ / .bashrc
Jackie Yeh

3
También puede usar un argumento (o dos ...), es decir:jhome(){ cd /home/tree/projects/$1; }
irbanana

1
Esta debería ser la forma correcta, esto hace posible el uso de caracteres especiales, como "-" por ejemplo.
bangkokguy

159

El cdse realiza dentro del shell del script. Cuando finaliza la secuencia de comandos, ese shell sale, y luego queda en el directorio en el que estaba. "Fuente" el script, no lo ejecutes. En vez de:

./myscript.sh

hacer

. ./myscript.sh

(Observe el punto y el espacio antes del nombre del script).


2
Esto es genial, y probablemente sea bueno saberlo. ¿Qué hace este último exactamente (cómo funciona)? Esta es probablemente la mejor solución en este hilo.
Jonás

2
fwiw, en la sección de comentarios de la respuesta de Adam Liss, ephemient responde a la pregunta de qué es el '. es. Es lo mismo quesource
g19fanatic

1
Tenga cuidado, "fuente" es un bashismo. es decir, el guión (valor predeterminado de Debian para / bin / sh) no lo admite, mientras que "." Funciona como se esperaba.
allo

¡Gran respuesta! Además, si myscript.shestá en un directorio incluido en $ PATH, puede obtenerlo desde cualquier lugar sin especificar la ruta completa.
CodeBrew

Esto funcionó para mí lo mejor. Esta solución es la más simple (+1).
HuserB1989

96

Para hacer un script bash que se cd a un directorio select:

Crea el archivo de script

#! / bin / sh
# archivo: / scripts / cdjava
# #
cd / inicio / askgelal / proyectos / java

Luego cree un alias en su archivo de inicio.

#! / bin / sh
# file /scripts/mastercode.sh
# #
alias cdjava = '. / scripts / cdjava '

  • Creé un archivo de inicio donde vuelco todos mis alias y funciones personalizadas.
  • Luego obtengo este archivo en mi .bashrc para configurarlo en cada arranque.

Por ejemplo, cree un archivo de alias / funciones maestro: /scripts/mastercode.sh ( coloque
el alias en este archivo).

Luego, al final de su archivo .bashrc :

fuente /scripts/mastercode.sh



Ahora es fácil de cd a su directorio de Java, simplemente escriba cdjava y ya está.


2
el archivo mastercode.shno necesita el shabang ( #!/bin/sh), ya que no se ejecuta (y no se puede) ejecutar en una subshell. Pero al mismo tiempo, debe documentar el "sabor" de la shell de este archivo; por ejemplo, ksh o bash (o (t) csh / zsh, etc.), y es casi seguro que en realidad no lo es sh. Usualmente agrego un comentario (pero no el shebang) para comunicar esto; por ejemplo, "este archivo debe tener su origen (de bash), no ejecutarse como un script de shell".
michael

Otro consejo (para bash): si usa variables en su secuencia de comandos, a medida que la secuencia de comandos se está sourced (a través de alias), esas variables se filtrarán en su entorno de shell. Para evitar eso, haga todo el trabajo del script en una función y simplemente llámelo al final del script. Dentro de la función, declare cualquier variable local.
Ruibarbo

en ubuntu solo cree ~ / .bash_aliases (si aún no existe). Luego solo agregue sus alias allí, reinicie el terminal y listo.
Vlad

51

Puede usar .para ejecutar un script en el entorno actual del shell:

. script_name

o, alternativamente, es un alias más legible pero específico de shell source:

source script_name

Esto evita el subshell y permite que cualquier variable o componente (incluido cd) afecte al shell actual.


38

La idea de Jeremy Ruten de usar un enlace simbólico desencadenó un pensamiento que no ha cruzado ninguna otra respuesta. Utilizar:

CDPATH=:$HOME/projects

El colon principal es importante; significa que si hay un directorio 'dir' en el directorio actual, entonces ' cd dir' cambiará a eso, en lugar de saltar a otro lado. Con el valor establecido como se muestra, puede hacer:

cd java

y, si no hay un subdirectorio llamado java en el directorio actual, lo llevará directamente a $ HOME / projects / java: sin alias, sin scripts, sin dudosos ejecutivos o comandos de puntos.

Mi $ HOME es / Usuarios / jleffler; mi $ CDPATH es:

:/Users/jleffler:/Users/jleffler/mail:/Users/jleffler/src:/Users/jleffler/src/perl:/Users/jleffler/src/sqltools:/Users/jleffler/lib:/Users/jleffler/doc:/Users/jleffler/work

31

Usar exec bashal final

Un script bash opera en su entorno actual o en el de sus hijos, pero nunca en su entorno principal.

Sin embargo, esta pregunta a menudo se hace porque uno quiere quedarse en un (nuevo) indicador de bash en un determinado directorio después de la ejecución de un script de bash desde otro directorio.

Si este es el caso, simplemente ejecute una instancia de bash secundaria al final del script:

#!/usr/bin/env bash
cd /home/tree/projects/java
exec bash

16
Esto crea una nueva subshell. Cuando escriba exit, volverá al shell donde ejecutó este script.
tripleee

1
Cuando se llamó varias veces al script, parece que crea shells anidados y necesito escribir 'exit' muchas veces. ¿Podría esto ser resuelto de alguna manera?
VladSavitsky

Vea las otras respuestas: use un alias, use "pushd / popd / dirs", o use "source"
qneill

25

Obtuve mi código para trabajar usando. <your file name>

./<your file name> la dosis no funciona porque no cambia su directorio en la terminal, solo cambia el directorio específico de ese script.

Aqui esta mi programa

#!/bin/bash 
echo "Taking you to eclipse's workspace."
cd /Developer/Java/workspace

Aqui esta mi terminal

nova:~ Kael$ 
nova:~ Kael$ . workspace.sh
Taking you to eclipe's workspace.
nova:workspace Kael$ 

2
¿Cuál es la diferencia entre . somethingy ./something?? Esta respuesta funcionó para mí y no entiendo por qué.
Dracorat

. somethingle permite ejecutar el script desde cualquier ubicación, ./somethingrequiere que esté en el directorio en el que está almacenado el archivo.
Projjol


¿Puedes poner el punto en la línea shebang? #!. /bin/bash?
Sigfried


12

Cuando activa un script de shell, ejecuta una nueva instancia de ese shell ( /bin/bash). Por lo tanto, su script solo enciende un shell, cambia el directorio y sale. Dicho de otra manera, cd(y otros comandos similares) dentro de un script de shell no afectan ni tienen acceso al shell desde el que se iniciaron.


11

En mi caso particular, necesitaba demasiadas veces cambiar para el mismo directorio. Entonces, en mi .bashrc (uso ubuntu) agregué el

1 -

$ nano ~. / bashrc

 function switchp
 {
    cd /home/tree/projects/$1
 }

2-

$ fuente ~ / .bashrc

3 -

$ switchp java

Directamente lo hará: cd / home / tree / projects / java

¡Espero que ayude!


1
@WM Eventualmente puede hacer "$ switchp" sin ningún parámetro para ir directamente al árbol del proyecto
workdreamer

2
@workdreamer El enfoque funcional que describió anteriormente resolvió un pequeño problema aquí al entrar en subcarpetas que tienen la misma carpeta principal en mi sistema. Gracias.
WM

10

Puedes hacer lo siguiente:

#!/bin/bash
cd /your/project/directory
# start another shell and replacing the current
exec /bin/bash

EDITAR: Esto también podría estar 'punteado', para evitar la creación de proyectiles posteriores.

Ejemplo:

. ./previous_script  (with or without the first line)

1
Eso se vuelve bastante desordenado después de unas pocas ejecuciones. Tendrá que exit(o ctrl + d) varias veces para salir del shell, por ejemplo ... Un alias es mucho más limpio (incluso si el comando del shell genera un directorio, y es cd's a la salida - alias something = "cd getnewdirectory.sh")
dbr

Tenga en cuenta el 'ejecutivo'. Hace reemplazar el viejo caparazón.
Thevs

2
El exec solo reemplaza el sub-shell que ejecutaba el comando cd, no el shell que ejecutaba el script. Si hubiera punteado el guión, estaría en lo correcto.
Jonathan Leffler el

Hazlo 'punteado': no ​​hay problema. Acabo de proponer una solución. No depende de cómo inicies esto.
Thevs

2
Jeje, creo que es mejor 'puntear' un script de una línea con solo el comando cd :)
Guardaré

7

Solo cambia el directorio del script en sí, mientras que su directorio actual permanece igual.

Es posible que desee utilizar un enlace simbólico en su lugar. Le permite hacer un "acceso directo" a un archivo o directorio, por lo que solo tendrá que escribir algo como cd my-project.


Tener ese enlace simbólico en cada directorio sería una molestia. Sería posible poner el enlace simbólico en $ HOME y luego hacer 'cd ~ / my-project'. Francamente, sin embargo, es más simple usar CDPATH.
Jonathan Leffler

7

Puede combinar el alias y los enfoques de punto de Adam y Greg para hacer algo que pueda ser más dinámico:

alias project=". project"

Ahora, al ejecutar el alias del proyecto, se ejecutará el script del proyecto en el shell actual en lugar del subshell.


Esto es exactamente lo que necesitaba para mantener todo mi script y simplemente llamarlo desde bash y mantener la sesión actual: esta es la respuesta PERFECTA.
Matt The Ninja

7

Puedes combinar un alias y un script,

alias proj="cd \`/usr/bin/proj !*\`"

siempre que la secuencia de comandos haga eco de la ruta de destino. Tenga en cuenta que esos son backticks que rodean el nombre del script. 

Por ejemplo, su script podría ser

#!/bin/bash
echo /home/askgelal/projects/java/$1

La ventaja de esta técnica es que el script puede tomar cualquier número de parámetros de línea de comando y emitir diferentes destinos calculados por una lógica posiblemente compleja.


3
¿por qué? simplemente puede usar: proj() { cd "/home/user/projects/java/$1"; }=> proj "foo"(o, proj "foo bar"<= en caso de que tenga espacios) ... o incluso (por ejemplo): proj() { cd "/home/user/projects/java/$1"; shift; for d; do cd "$d"; done; }=> proj a b c=> hace un cdinto/home/user/projects/java/a/b/c
michael


5

En su archivo ~ / .bash_profile. agrega la siguiente función

move_me() {
    cd ~/path/to/dest
}

Reinicie la terminal y puede escribir

move_me 

y será trasladado a la carpeta de destino.


3

Puede usar el operador &&:

cd myDirectory && ls


Voto a favor: Esto no intenta responder la pregunta real aquí. Puede ejecutar dos comandos en el indicador (con &&si desea que el segundo sea condicional al primero, o simplemente con ;o una nueva línea entre ellos) pero poner esto en un script lo llevará de regreso a "¿por qué no utiliza el shell principal? el nuevo directorio cuando el cdfue realmente exitoso? "
tripleee

3

Si bien la fuente del script que desea ejecutar es una solución, debe tener en cuenta que este script puede modificar directamente el entorno de su shell actual. Además, ya no es posible pasar argumentos.

Otra forma de hacerlo es implementar su script como una función en bash.

function cdbm() {
  cd whereever_you_want_to_go
  echo "Arguments to the functions were $1, $2, ..."
}

Autojump utiliza esta técnica: http://github.com/joelthelion/autojump/wiki para proporcionarle marcadores de directorio de shell de aprendizaje.


3

Puede crear una función como la siguiente en su .bash_profile y funcionará sin problemas.

La siguiente función toma un parámetro opcional que es un proyecto. Por ejemplo, solo puedes ejecutar

cdproj

o

cdproj project_name

Aquí está la definición de la función.

cdproj(){
    dir=/Users/yourname/projects
    if [ "$1" ]; then
      cd "${dir}/${1}"
    else
      cd "${dir}"
    fi
}

No olvides buscar tu fuente .bash_profile


1
respuesta mucho mejor que alias
Pax0r

Esta solución es brillante en su simplicidad y flexibilidad.
Kjartan

3

Esto debería hacer lo que quieras. Cambie al directorio de interés (desde dentro del script) y luego genere un nuevo shell bash.

#!/bin/bash

# saved as mov_dir.sh
cd ~/mt/v3/rt_linux-rt-tools/
bash

Si ejecuta esto, lo llevará al directorio de interés y, cuando salga, lo llevará de regreso al lugar original.

root@intel-corei7-64:~# ./mov_dir.sh

root@intel-corei7-64:~/mt/v3/rt_linux-rt-tools# exit
root@intel-corei7-64:~#

Esto incluso lo llevará a volver a su directorio original cuando salga ( CTRL+ d)


3
Generar una nueva fiesta significará que pierdes tu historial y comenzará de nuevo.
Tisch

2

MUCHO tiempo después, pero hice lo siguiente:

crear un archivo llamado caso

pegue lo siguiente en el archivo:

#!/bin/sh

cd /home/"$1"

guárdelo y luego:

chmod +x case

También creé un alias en mi .bashrc:

alias disk='cd /home/; . case'

ahora cuando escribo:

case 12345

esencialmente estoy escribiendo:

cd /home/12345

Puede escribir cualquier carpeta después de 'case':

case 12

case 15

case 17

que es como escribir:

cd /home/12

cd /home/15

cd /home/17

respectivamente

En mi caso, el camino es mucho más largo: estos muchachos lo resumieron con la información ~ anterior.


1
El cden el alias es superfluo y poco elegante; el alias debería simplemente ". ~ / case` en su lugar. También casees una palabra clave reservada, por lo que es una elección bastante pobre para un nombre.
tripleee

1

No necesita script, solo configure la opción correcta y cree una variable de entorno.

shopt -s cdable_vars

en su ~/.bashrcpermite al cdcontenido de variables de entorno.

Cree una variable de entorno de este tipo:

export myjava="/home/tree/projects/java"

y puedes usar:

cd myjava

Otras alternativas .


0

Si está utilizando fish como su caparazón, la mejor solución es crear una función. Como ejemplo, dada la pregunta original, puede copiar las 4 líneas a continuación y pegarlas en su línea de comando de pescado:

function proj
   cd /home/tree/projects/java
end
funcsave proj

Esto creará la función y la guardará para su uso posterior. Si su proyecto cambia, simplemente repita el proceso utilizando la nueva ruta.

Si lo prefiere, puede agregar manualmente el archivo de función haciendo lo siguiente:

nano ~/.config/fish/functions/proj.fish

e ingrese el texto:

function proj
   cd /home/tree/projects/java
end

y finalmente presione ctrl + x para salir e y seguido de regresar para guardar sus cambios

( NOTA: el primer método de usar funcsave crea el archivo proj.fish para usted ).


0

Tengo un script bash simple llamado p para administrar el cambio de directorio en
github.com/godzilla/bash-stuff,
simplemente coloque el script en su directorio bin local (/ usr / local / bin)
y coloque

alias p='. p'

en tu .bashrc


que hace esto parece que se ejecutaría de forma recursiva, aunque estoy seguro de que si lo has ejecutado antes;)
Ryan Taylor


0

Es una vieja pregunta, pero estoy realmente sorprendido de no ver este truco aquí.

En lugar de usar cd puedes usar

export PWD=the/path/you/want

No es necesario crear subcapas o usar alias.

Tenga en cuenta que es su responsabilidad asegurarse de que exista / path / you / want.


No funcionó, desafortunadamente.
ttfreeman

0

Como se explica en las otras respuestas, ha cambiado el directorio, pero solo dentro del subconjunto que ejecuta el script . Esto no afecta el shell padre.

Una solución es usar funciones bash en lugar de un script bash ( sh); colocando su código de script bash en una función. Eso hace que la función esté disponible como un comando y luego, esto se ejecutará sin un proceso secundario y, por lo tanto, cualquier cdcomando afectará al shell de la persona que llama.

Funciones bash:

Una característica del perfil bash es almacenar funciones personalizadas que pueden ejecutarse en el terminal o en scripts bash de la misma manera que ejecuta aplicaciones / comandos, esto también podría usarse como un acceso directo para comandos largos.

Para hacer que su sistema sea eficiente en gran medida, deberá copiar su función al final de varios archivos

/home/user/.bashrc
/home/user/.bash_profile
/root/.bashrc
/root/.bash_profile

Puedes sudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profileeditar / crear esos archivos rápidamente

Cómo :

Copie su código de script bash dentro de una nueva función al final del archivo de perfil de su bash y reinicie su terminal, luego puede ejecutar cddo cualquiera que sea la función que escribió.

Ejemplo de guion

Hacer acceso directo a cd ..concdd

cdd() {
  cd ..
}

atajo de ls

ll() {
  ls -l -h
}

atajo de ls

lll() {
  ls -l -h -a
}

-2

Puede ejecutar algunas líneas en la misma subshell si termina las líneas con una barra diagonal inversa.

cd somedir; \
pwd
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.