Por que "cd" não funciona em um shell script?


767

Estou tentando escrever um pequeno script para alterar o diretório atual para o diretório do meu projeto:

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

Salvei este arquivo como proj, adicionei permissão de execução chmode copiei-o para /usr/bin. Quando eu chamo por proj:, não faz nada. O que estou fazendo errado?


10
duplicado entre sites: superuser.com/questions/176783/…
lesmana

No futuro, você sempre pode tentar testá-lo pwdna última linha. Portanto, antes de terminar roteiro, então você pode verificar é trabalhar ou não ..
sobi3ch

5
@lesmana como isso é uma duplicata?
Aland

@aland Como o OP de fato não executa o script, é por isso que o diretório de trabalho não muda para ele. cdcomando funciona bem dentro de scripts, tente você mesmo.
Alexander Gonchiy

Respostas:


635

Os scripts de shell são executados dentro de um subshell, e cada subshell tem seu próprio conceito de qual é o diretório atual. O resultado cdé bem-sucedido, mas assim que o subshell sai, você volta ao shell interativo e nada muda lá.

Uma maneira de contornar isso é usar um alias:

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

7
Os aliases não são tão flexíveis para gerenciar ou alterar. No caso de muitos CDs, os scripts poderiam ser melhores.
Thevs

16
As funções são mais flexíveis do que aliases, então é nesse ponto que você procuraria a seguir quando os aliases não forem suficientes.
#

4
Vale a pena notar que no MS-DOS, o comportamento dos scripts era que um script chamado poderia alterar o diretório (e até a unidade) do shell de comando de chamada? E que o Unix não tem esse defeito?
Jonathan Leffler

23
Jonathan: enquanto isso é verdade, não está realmente relacionado à questão. As respostas no SO teriam o dobro do tempo se elas tivessem para cada lista as deficiências correspondentes no MS-DOS!
Greg Hewgill 04/11/2008

17
Outra maneira de contornar isso é obter o arquivo de script: . my-scriptor source my-script.
hellogoodbye

504

Você não está fazendo nada errado! Você alterou o diretório, mas apenas dentro do subshell que executa o script.

Você pode executar o script no seu processo atual com o comando "ponto":

. proj

Mas eu prefiro a sugestão de Greg de usar um pseudônimo neste caso simples.


95
.também estiver escrito source, escolha o que achar mais memorável.
#

47
@Ephemient: Boa PointSource Isso explica por que workssource também prova que a preguiça-não necessidade-é muitas vezes a mãe de inventionsource
Adam Liss

8
@hemhemient: note que a fonte é usada no shell C e no Bash; não é suportado em shells POSIX ou Korn, nem no shell Bourne clássico.
Jonathan Leffler

2
"fonte" é usado em Z-shell
Nathan Moos

1
@AdamLiss Funciona muito bem, mas há uma desvantagem - de alguma forma, o comando source / dot desabilita a possibilidade de concluir automaticamente o nome do script / comando com a tecla TAB. Ainda é possível adicionar argumentos / entrada com a tecla TAB, mas infelizmente o nome do comando precisa ser inserido diretamente. Você sabe como fazer a tecla TAB funcionar nesse caso?
Rafal 23/01

215

O cdem seu script tecnicamente trabalhou como ele mudou o diretório do shell que executou o script, mas isso foi um processo separado bifurcada do seu shell interativo.

Uma maneira compatível com Posix para resolver esse problema é definir um procedimento de shell em vez de um script de comando invocado por shell .

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

Você pode digitar ou colocar em um dos vários arquivos de inicialização do shell.


6
Concordo, esta é a melhor resposta, esbarrou. Além disso, o alias pode ser apropriado em algumas situações, mas se ele estiver tentando usar o cd em um script e quiser adicionar mais alguma coisa, um alias seria inútil.
23713 Justin Buser

4
Parece uma solução! E eu estou tentando implementá-lo, mas ele não está executando :( aqui está o meu script: #! / Bin / bash jhome () {echo "please" cd / home echo "work"} jhome
Gant Laborde

1
@GantMan, você deve adicionar isso em "arquivos de inicialização do shell", como ~ / .bashrc
Jackie Yeh

3
Você também pode usar um argumento (ou dois ...), ou seja:jhome(){ cd /home/tree/projects/$1; }
irbanana 12/02/16

1
Esse deve ser o caminho certo, possibilitando o uso de caracteres especiais, como "-" por exemplo.
bangkokguy

159

Isso cdé feito dentro do shell do script. Quando o script termina, esse shell sai e você é deixado no diretório em que estava. "Origem" do script, não o execute. Ao invés de:

./myscript.sh

Faz

. ./myscript.sh

(Observe o ponto e o espaço antes do nome do script.)


2
Isso é legal e provavelmente bom saber. Mas o que o último faz exatamente (como funciona)? Esta é provavelmente a melhor solução para este segmento.
Jonah

2
fwiw, na seção de comentários da resposta de Adam Liss, ephemient responde à pergunta de qual é o '.' é. É a mesma coisa quesource
g19fanatic

1
Cuidado, "fonte" é um basismo. isto é, dash (padrão do Debian para / bin / sh) não o suporta, enquanto "." funciona como esperado.
allo

Ótima resposta! Além disso, se myscript.shestiver em um diretório incluído no $ PATH, você poderá obtê-lo de qualquer lugar sem especificar o caminho completo.
CodeBrew 6/02/19

Isso funcionou para mim o melhor. Esta solução é a mais simples (+1).
HuserB1989

96

Para criar um script bash que fará o cd para um diretório selecionado:

Crie o arquivo de script

#! / bin / sh
# file: / scripts / cdjava
#
cd / home / askgelal / projetos / java

Em seguida, crie um alias no seu arquivo de inicialização.

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

  • Criei um arquivo de inicialização em que despejo todos os meus aliases e funções personalizadas.
  • Depois, forneço esse arquivo no meu .bashrc para defini-lo em cada inicialização.

Por exemplo, crie um arquivo de aliases / funções mestre: /scripts/mastercode.sh
(coloque o alias neste arquivo.)

Em seguida, no final do seu arquivo .bashrc :

source /scripts/mastercode.sh



Agora é fácil fazer o cd no diretório java, basta digitar cdjava e você estará lá.


2
o arquivo mastercode.shnão precisa do shabang ( #!/bin/sh), pois não é (e não pode ser) executado em um subshell. Mas, ao mesmo tempo, você precisa documentar o "sabor" do shell desse arquivo; por exemplo, ksh ou bash (ou (t) csh / zsh, etc), e quase certamente não é realmente sh. Eu costumo adicionar um comentário (mas não o shebang) para comunicar isso; por exemplo, "este arquivo deve ser originado (do bash), não executado como um script de shell".
22613 Michael

Outra dica (para o bash): se você usar variáveis ​​em seu script, como o script está sendo sourced (via the alias), essas variáveis ​​vazarão no seu ambiente de shell. Para evitar isso, faça todo o trabalho do script em uma função e chame-o no final do script. Dentro da função, declare quaisquer variáveis local.
Rhubbarb 17/04/2015

no ubuntu basta criar ~ / .bash_aliases (se já não existir). Em seguida, basta adicionar seus apelidos lá, reiniciar o terminal e pronto.
Vlad

51

Você pode usar .para executar um script no ambiente atual do shell:

. script_name

ou, alternativamente, seu alias mais legível, mas específico do shell source:

source script_name

Isso evita o subshell e permite que quaisquer variáveis ​​ou componentes internos (inclusive cd) afetem o shell atual.


38

A idéia de Jeremy Ruten de usar um link simbólico desencadeou um pensamento que não passou por nenhuma outra resposta. Usar:

CDPATH=:$HOME/projects

O cólon principal é importante; significa que, se houver um diretório 'dir' no diretório atual, ' cd dir' será alterado para isso, em vez de sair para outro lugar. Com o valor definido como mostrado, você pode:

cd java

e, se não houver um subdiretório chamado java no diretório atual, ele o levará diretamente para $ HOME / projects / java - sem alias, sem scripts, sem executivos duvidosos ou comandos de ponto.

Meu $ HOME é / Users / jleffler; meu $ CDPATH é:

:/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

Use exec bashno final

Um script bash opera em seu ambiente atual ou em seus filhos, mas nunca em seu ambiente pai.

No entanto, essa pergunta geralmente é feita porque alguém deseja ser deixado em um (novo) prompt do bash em um determinado diretório após a execução de um script bash de outro diretório.

Se for esse o caso, basta executar uma instância do bash filho no final do script:

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

16
Isso cria um novo subshell. Ao digitar, exitvocê retornará ao shell em que executou esse script.
Tripleee

1
Quando o script foi chamado várias vezes, parece criar shells aninhados e preciso digitar 'exit' várias vezes. Isso poderia ser resolvido de alguma forma?
VladSavitsky

Veja as outras respostas: use um alias, use "pushd / popd / dirs" ou use "source"
qneill 03/02

25

Eu tenho meu código para trabalhar usando. <your file name>

./<your file name> Dose não funciona porque não altera seu diretório no terminal, apenas altera o diretório específico para esse script.

Aqui está o meu programa

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

Aqui está o meu terminal

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

2
Qual é a diferença entre . somethinge ./something?? Essa resposta funcionou para mim e não entendo o porquê.
Dracorat 02/11/2012

. somethingpermite-lhe executar o roteiro de qualquer lugar, ./somethingexige que você para estar no diretório do arquivo é armazenado no.
Projjol


Você pode colocar o ponto na linha shebang? #!. /bin/bash?
Sigfried 19/04


12

Quando você aciona um script de shell, ele executa uma nova instância desse shell ( /bin/bash). Assim, seu script apenas dispara um shell, altera o diretório e sai. Em cdoutras palavras , (e outros comandos desse tipo) em um script de shell não afetam nem têm acesso ao shell a partir do qual foram iniciados.


11

No meu caso particular, eu precisei muitas vezes para alterar para o mesmo diretório. Então, no meu .bashrc (eu uso o ubuntu), adicionei o

1 -

$ nano ~ / bashrc

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

2-

$ fonte ~ / .bashrc

3 -

$ switchp java

Diretamente ele fará: cd / home / tree / projects / java

Espero que ajude!


1
@WM Você pode, eventualmente, fazer "$ switchp" sem qualquer parâmetro para ir diretamente para a árvore do projeto
workdreamer

2
@workdreamer A abordagem de função que você descreveu acima resolveu um pequeno problema aqui ao entrar em subpastas que possuem a mesma pasta pai no meu sistema. Obrigado.
WM

10

Você pode fazer o seguinte:

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

EDIT: Isso também pode ser 'pontilhado', para impedir a criação de shells subsequentes.

Exemplo:

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

1
Isso fica um pouco confuso após algumas execuções. Você precisará exit(ou ctrl + d) várias vezes para sair do shell, por exemplo. Um alias é muito mais limpo (mesmo que o comando shell produza um diretório e ele para a saída - alias something = "cd getnewdirectory.sh")
dbr

Observe o 'exec'. Faz substituir de casca velha.
Thevs

2
O exec substitui apenas o sub-shell que estava executando o comando cd, não o shell que executou o script. Se você tivesse pontilhado o script, estaria correto.
Jonathan Leffler

Faça com que seja pontilhado - não há problema. Eu apenas propus uma solução. Não depende de como você inicia isso.
Thevs

2
Hehe, eu acho que é melhor apenas para 'dot' script de uma linha com apenas o comando cd :) Vou manter a minha resposta de qualquer maneira ... Isso vai ser a resposta estúpida correta para pergunta estúpida incorreta :)
Thevs

7

Ele altera apenas o diretório do próprio script, enquanto o diretório atual permanece o mesmo.

Você pode usar um link simbólico . Ele permite que você faça um "atalho" para um arquivo ou diretório, então você só precisa digitar algo parecido cd my-project.


Ter esse link simbólico em todos os diretórios seria um incômodo. Seria possível colocar o link simbólico em $ HOME e depois 'cd ~ / my-project'. Francamente, porém, é mais simples usar o CDPATH.
Jonathan Leffler

7

Você pode combinar o alias de Adam & Greg e as abordagens de pontos para criar algo que possa ser mais dinâmico.

alias project=". project"

Agora, a execução do alias do projeto executará o script do projeto no shell atual, em oposição ao subshell.


Era exatamente isso que eu precisava para manter todo o meu script e apenas chamá-lo do bash e manter a sessão atual - esta é a resposta PERFEITA.
Matt A Ninja

7

Você pode combinar um alias e um script,

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

desde que o script faça eco no caminho de destino. Observe que esses são backticks que cercam o nome do script. 

Por exemplo, seu script pode ser

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

A vantagem dessa técnica é que o script pode pegar qualquer número de parâmetros de linha de comando e emitir destinos diferentes calculados por uma lógica possivelmente complexa.


3
porque? você pode simplesmente usar: proj() { cd "/home/user/projects/java/$1"; }=> proj "foo"(ou, proj "foo bar"<= no caso de você ter espaços) ... ou até mesmo (por exemplo): proj() { cd "/home/user/projects/java/$1"; shift; for d; do cd "$d"; done; }=> proj a b c=> faz um cdinto/home/user/projects/java/a/b/c
Michael


5

No seu arquivo ~ / .bash_profile. adicione a próxima função

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

Reinicie o terminal e você pode digitar

move_me 

e você será movido para a pasta de destino.


3

Você pode usar o operador &&:

cd myDirectory && ls


Downvote: Isso não tenta responder à pergunta real aqui. Você pode executar dois comandos no prompt (com &&se desejar que o segundo seja condicional no primeiro, ou apenas com ;ou nova linha entre eles), mas colocar isso em um script o levará de volta a "por que o shell pai não usa" o novo diretório quando o cdfoi realmente bem-sucedido? "
Tripleee

3

Embora a fonte do script que você deseja executar seja uma solução, você deve estar ciente de que esse script pode modificar diretamente o ambiente do seu shell atual. Também não é mais possível passar argumentos.

Outra maneira de fazer isso é implementar seu script como uma função no bash.

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

Essa técnica é usada pelo autojump: http://github.com/joelthelion/autojump/wiki para fornecer a você os indicadores de diretório de shell de aprendizado.


3

Você pode criar uma função como abaixo na sua .bash_profilee ela funcionará sem problemas.

A função a seguir aceita um parâmetro opcional que é um projeto. Por exemplo, você pode simplesmente executar

cdproj

ou

cdproj project_name

Aqui está a definição da função.

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

Não se esqueça de adquirir seu .bash_profile


1
resposta muito melhor do que o apelido #
Pax0r 28/10/16

Esta solução é brilhante em sua simplicidade e flexibilidade.
Kjartan

3

Isso deve fazer o que você quiser. Vá para o diretório de interesse (de dentro do script) e crie um novo shell bash.

#!/bin/bash

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

Se você executar isso, ele o levará ao diretório de interesse e, quando você o sair, o levará de volta ao local original.

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

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

Isso levará você a voltar ao diretório original quando sair ( CTRL+ d)


3
Desovar uma nova festança significa que você perderá sua história e ela começará de novo.
Tisch

2

LOOOOOng depois, mas fiz o seguinte:

crie um arquivo chamado case

cole o seguinte no arquivo:

#!/bin/sh

cd /home/"$1"

salve-o e depois:

chmod +x case

Eu também criei um alias no meu .bashrc:

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

agora quando digito:

case 12345

essencialmente eu estou digitando:

cd /home/12345

Você pode digitar qualquer pasta após 'case':

case 12

case 15

case 17

que é como digitar:

cd /home/12

cd /home/15

cd /home/17

respectivamente

No meu caso, o caminho é muito mais longo - esses caras resumiram o ~ info anteriormente.


1
O cdno alias é supérfluo e deselegante; o apelido deve simplesmente '. ~ / case`. Também caseé uma palavra-chave reservada, portanto, uma escolha bastante ruim para um nome.
Tripleee 29/07

1

Você não precisa de script, apenas defina a opção correta e crie uma variável de ambiente.

shopt -s cdable_vars

no seu ~/.bashrcpermite ao cdconteúdo de variáveis ​​de ambiente.

Crie uma variável de ambiente:

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

e você pode usar:

cd myjava

Outras alternativas .


0

Se você estiver usando peixe como casca, a melhor solução é criar uma função. Como exemplo, dada a pergunta original, você pode copiar as 4 linhas abaixo e colá-las na linha de comando do peixe:

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

Isso criará a função e a salvará para uso posterior. Se o seu projeto mudar, basta repetir o processo usando o novo caminho.

Se preferir, você pode adicionar manualmente o arquivo de função, fazendo o seguinte:

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

e digite o texto:

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

e, finalmente, pressione ctrl + x para sair ey seguido de return para salvar suas alterações.

( NOTA: o primeiro método de usar o funcsave cria o arquivo proj.fish para você ).


0

Eu tenho um script bash simples chamado p para gerenciar a alteração de diretório no
github.com/godzilla/bash-stuff,
basta colocar o script no diretório bin local (/ usr / local / bin)
e colocar

alias p='. p'

no seu .bashrc


o que isso faz? parece que ele iria de forma recursiva executar-se, embora eu tenho certeza que se você executá-lo antes que ele não o faz;)
Ryan Taylor


0

É uma pergunta antiga, mas estou realmente surpreso por não ver esse truque aqui

Em vez de usar cd, você pode usar

export PWD=the/path/you/want

Não há necessidade de criar subshells ou usar aliases.

Observe que é sua responsabilidade garantir que o / path / you / want exista.


Infelizmente não funcionou.
ttfreeman 03/04

0

Conforme explicado nas outras respostas, você alterou o diretório, mas apenas dentro do sub-shell que executa o script . isso não afeta o shell pai.

Uma solução é usar funções bash em vez de um script bash ( sh); colocando seu código de script bash em uma função. Isso torna a função disponível como um comando e, em seguida, isso é executado sem um processo filho e, portanto, qualquer cdcomando afeta o shell do chamador.

Funções do Bash:

Um recurso do perfil bash é armazenar funções personalizadas que podem ser executadas no terminal ou em scripts bash da mesma maneira que você executa aplicativos / comandos. Isso também pode ser usado como um atalho para comandos longos.

Para tornar seu sistema eficiente de funções amplamente, você precisará copiar sua função no final de vários arquivos

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

Você pode sudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profileeditar / criar esses arquivos rapidamente

Como :

Copie o código do seu script bash dentro de uma nova função no final do arquivo de perfil do bash e reinicie o terminal. Depois, você pode executar cddou qualquer que seja a função que você escreveu.

Exemplo de script

Fazendo atalho para cd ..comcdd

cdd() {
  cd ..
}

ls atalho

ll() {
  ls -l -h
}

ls atalho

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

-2

Você pode executar algumas linhas no mesmo subshell se terminar as linhas com barra invertida.

cd somedir; \
pwd
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.