Como posso criar meus próprios "comandos de shell" (por exemplo, mkdir / cd combo)?


41

Não sei dizer quantas vezes desejei um comando que crie um diretório e mude para esse diretório. Basicamente, eu gostaria do equivalente ao seguinte:

mkdir -p /arbitrarily/long/path; cd /arbitrarily/long/path

mas apenas tendo que digitar /arbitrarily/long/pathuma vez, algo como:

mk-cd /arbitrarily/long/path

Tentei criar um script para fazer isso, mas ele apenas altera o diretório dentro do script. Eu gostaria que o diretório no shell também tivesse mudado.

#!/bin/bash
mkdir $1
cd $1
export PWD=$PWD

Como eu posso fazer isso funcionar?


12
Com cd, você escolheu um caso especial desde o início. : D
— Daniel B

2
Não é exatamente o que eu estava procurando, mas cdinformações relacionadas a informações muito legais (retorne ao diretório anterior usando cd -, use pushde popdpara manter uma "pilha" de diretórios): superuser.com/questions/324512/…
— Christopher Bottoms

8
Você pode digitar mkdir -p /very/long/path, usar cdespaço e pressionar Alt + .para repetir o último argumento, ou seja, o nome do diretório.
— choroba

2
Não é uma resposta, apenas um comentário semelhante ao @ choroba: Você também pode usar mkdir -p /very/long/path; cd !#:2. A string !#:2será expandida para o argumento nr. 2 (ou seja, o terceiro argumento /very/long/path, conforme a contagem começa com zero).
— Ingo Blechschmidt

3
@IngoBlechschmidt, ainda mais fácil, já que é o último argumento do último comando, você pode apenas usar !$. Eu uso esse truque em particular o tempo todo, embora haja muito mais a fazer com a expansão do histórico .
— Curinga

Respostas:


92

A maneira mais simples é usar uma função shell:

mkcd() {
    mkdir -p -- "$1" && cd -- "$1"
}

Coloque-o no seu .bashrcarquivo para torná-lo disponível, assim como outro comando do shell.

A razão pela qual ele não funciona como um script externo é cdaltera o diretório atual do script em execução, mas não afeta o chamado. Isso é por design! Cada processo tem seu próprio diretório de trabalho que é herdado por seus filhos, mas o oposto não é possível.

A menos que parte de um pipeline seja executado em segundo plano ou explicitamente em um subshell, uma função shell não é executada em um processo separado, mas no mesmo, como se o comando tivesse sido originado. O shell do diretório atual pode ser alterado por uma função.

O &&usado aqui para separar os dois comandos utilizados significa que, se o primeiro comando for bem-sucedido ( mkdir), execute o segundo ( cd). Consequentemente, se mkdirnão conseguir criar o diretório solicitado, não faz sentido tentar entrar nele. Uma mensagem de erro é impressa mkdire é isso.

A -popção usada com mkdirexiste para informar este utilitário para criar qualquer diretório ausente que faça parte do caminho completo do nome do diretório passado como argumento. Um efeito colateral é que, se você pedir para criar um diretório já existente, a mkcdfunção não falhará e você terminará nesse diretório. Isso pode ser considerado um problema ou um recurso. No primeiro caso, a função pode ser modificada, por exemplo, da maneira que simplesmente avisa o usuário:

mkcd() {
    if [ -d "$1" ]; then
        printf "mkcd: warning, \"%s\" already exists\n" "$1"
    else
        mkdir -p "$1" 
    fi && cd "$1"
}

Sem a -popção, o comportamento da função inicial teria sido muito diferente.

Se o diretório que contém o diretório a ser criado ainda não existir, mkdira função também falhará.

Se o diretório a ser criado já existir, mkdirfalhará também e cdnão será chamado.

Por fim, observe que a configuração / exportação PWDé inútil, como o shell já faz internamente.

Editar: adicionei a --opção aos dois comandos da função para permitir um nome de diretório que comece com um traço.


5
Você também deve adicionar que esse comando não ficará disponível permanentemente, a menos que seja adicionado a ~/.bashrcou um dos outros scripts de inicialização.
— AFH

Não existe maneira no bash de fazer o equivalente a tcsh's alias mk-cd 'mkdir -p \!:1; cd \!:1'sem uma função? Ou talvez eu devesse começar a pensar nas funções do bash mais como aliases estendidos?
— Thomas Padron-McCarthy

@ ThomasPadron-McCarthy Este cshmecanismo de passagem de argumentos não é implementado com aliases de estilo bourne. As funções são realmente o caminho a percorrer, sendo muito mais flexíveis.
— Jlliagre

@ ThomasPadron-McCarthy - Você pode olhar para esta resposta , que fornece um mecanismo bastante obscuro para acessar parâmetros passados ​​para um alias (na definição de bar). Outra opção seria usar history 1, como em alias mk-cd='args="$(history 1)" && args="${args#*mk-cd\ }" && mkdir -p "$args" && cd'- isso funciona bem para o exemplo do questionador, mas não é uma solução geral, pois qualquer outro comando na mesma linha (por exemplo, canalizar a saída) fará com que ela falhe.
— AFH

3
@ ThomasPadron-McCarthy Você deve começar a pensar nos pseudônimos do tcsh mais como funções restritas.
— Gilles 'SO- stop be evil'

32

Eu gostaria de adicionar uma solução alternativa.

Seu script vai funcionar se você invocá-lo com o . ou sourcecomando:

. mk-cd /arbitrarily/long/path

Se você fizer isso export, o script não será necessário. Você pode ignorar a digitação .usando um alias:

alias mk-cd='. mk-cd'

A razão pela qual isso funciona é que um script normalmente é executado em um sub shell, para que seu ambiente seja perdido na conclusão, mas o comando .(ou source) o força a executar no shell atual.

Essa técnica pode ser útil para seqüências de comandos que são muito complexas para uma função ou que estão em desenvolvimento e são frequentemente editadas: uma vez aliasinserida .bash_aliases, você pode editar o script à vontade sem reinicializar.

Observe o seguinte: -

Se você escrever um script que deve ser chamado com o comando ./ source, há duas maneiras de garantir isso:

  1. Por alguma razão, um script invocado com ./ sourcenão precisa ser executável; portanto, remova essa permissão e o script não será encontrado em "$ PATH" ou causará um erro de permissão se invocado com um caminho explícito.

  2. Como alternativa, o seguinte truque pode ser usado na cabeça do script:

bind |& read && { echo 'Must be run from "." or "source" command'; exit 1; }

Isso funciona porque em um sub-shell bindemite um aviso, embora não haja status de erro: portanto, readé usado para dar um erro em nenhuma entrada.


Obrigado pela informação em .. Já fiz . .bashrcinúmeras vezes, mas não sabia exatamente o que .faz. É semelhante a export?
— cst1992

2
@ cst1992 - Não, os comandos são completamente diferentes: export varcopia a variável interna varpara o ambiente, onde é herdada e importada como variável em qualquer sub shell, mas não tem efeito em um shell pai. Normalmente, um sub-shell é criado para executar um script e todas as alterações que ele faz (incluindo variáveis ​​exportadas) são perdidas quando concluídas. Para que um script defina variáveis ​​em um shell, ele deve ser executado nesse shell, e é isso que o .comando faz: execute o script sem criar um sub shell. Curiosamente, os scripts executados por .não precisam ter permissão executável.
— AFH

Obrigado pela informação. Eu insisto que você inclua essas informações em sua postagem. Isso é útil e, francamente, os comentários não têm garantia de que eles estarão lá amanhã.
— cst1992

@ cst1992 - Abordei a pergunta original, mas não quero confundir minha resposta com questões acessórias. Se você estivesse procurando uma explicação dos comandos .e export, o título desta pergunta não levaria você a esperar uma resposta aqui, então deixarei minha resposta como está, mas obrigado pelo seu comentário.
— AFH

+1 em geral, mas especialmente para o alias. Eu tenho alguns scripts que precisam ser adquiridos e sempre esqueço de executá-los dessa maneira. O alias cuida disso muito bem.
— Joe

13

Não é um comando único, mas você não precisa digitar novamente todo o caminho usado !$(que é o último argumento do comando anterior no histórico do shell).

Exemplo:

mkdir -p /arbitrarily/long/path
cd !$

1

Criando aliases . Maneira muito popular de criar seus próprios comandos.

Você pode criar um alias rapidamente:

alias myalias='mkdir -p /arbitrarily/long/path; cd /arbitrarily/long/path'

É isso aí. Se você deseja manter esse alias permanentemente (não apenas a sessão atual), coloque esse comando em um arquivo de pontos (normalmente ~ / .bash_aliases ou ~ / .bash_profile).


4
Como o nome do diretório longo é codificado permanentemente, esse alias não seria muito útil, a menos que este diretório seja destruído regularmente por algum outro processo.
— Jlliagre # 9/16

1

Além do que já foi sugerido, eu gostaria de recomendar o caralho . É uma estrutura Python para otimizar seu processo de shell, corrigindo erros. Inspirado por esse tweet , tudo o que você precisa fazer é digitar seu alias configurado (por padrão fuck) e ele tentará corrigir o comando anterior. Uma de suas correções se comporta da seguinte maneira:

/etc $ cd somedir
bash: cd: No such file or directory
/etc $ fuck
mkdir somedir && cd somedir
/etc/somedir $ 
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.