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

41

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

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

mas só precisa digitar /arbitrarily/long/path uma vez, algo como:

mk-cd /arbitrarily/long/path

Eu 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 posso fazer isso funcionar?

    
por Christopher Bottoms 06.05.2016 / 15:09

5 respostas

92

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

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

Coloque-o no seu arquivo .bashrc para torná-lo disponível para você como outro comando de shell.

O motivo pelo qual ele não funciona como um script externo é cd altera o diretório atual do script em execução, mas não afeta o diretório atual. Isso é por design! Cada processo tem seu próprio diretório de trabalho que é herdado por seus filhos, mas o contrário não é possível.

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

O && usado aqui para separar os dois comandos usados significa que, se o primeiro comando for bem-sucedido ( mkdir ), execute o segundo ( cd ). Conseqüentemente, se mkdir falhar ao criar o diretório solicitado, não faz sentido tentar entrar nele. Uma mensagem de erro é impressa por mkdir e é isso.

A opção -p usada com mkdir está lá para dizer a esse 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 função mkcd 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, o 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 opção -p , 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, mkdir falhará, assim como a função.

Se o diretório a ser criado já existir, mkdir também falhará e cd não será chamado.

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

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

    
por 06.05.2016 / 15:20
32

Eu gostaria de adicionar uma solução alternativa.

Seu script funcionará se você invocá-lo com o comando . ou source :

. mk-cd /arbitrarily/long/path

Se você fizer isso, o export no script será desnecessário. Você pode ignorar a digitação de . usando alias :

alias mk-cd='. mk-cd'

A razão pela qual isso funciona é que um script normalmente é executado em um sub-shell, então seu ambiente é perdido após a conclusão, mas o comando . (ou source ) força a execução 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 editadas com frequência: assim que o alias for inserido em .bash_aliases , você poderá editar o script sem reinicializar .

Observe o seguinte: -

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

  1. Por algum motivo, um script chamado com . / source não precisa ser executável, portanto, basta remover essa permissão e o script não será encontrado em "$ PATH" ou dará 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 bind emite um aviso, embora nenhum status de erro: portanto, read é usado para fornecer um erro em nenhuma entrada.

    
por 06.05.2016 / 16:38
13

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

Exemplo:

mkdir -p /arbitrarily/long/path
cd !$
    
por 08.05.2016 / 20:05
1

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

Você pode criar um alias imediatamente:

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

É isso. Se você quiser 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).

    
por 07.05.2016 / 18:53
1

Além do que já foi sugerido, gostaria de recomendar thefuck . É uma estrutura Python para simplificar o processo do shell, corrigindo erros. Inspirado por este tweet , tudo que você precisa fazer é digitar seu alias configurado (por padrão fuck ) e ele tentará para corrigir seu 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 $ 
    
por 08.05.2016 / 18:34

Tags