No Bash, quando alias, quando script e quando escrever uma função?

360

Levei quase 10 anos de uso do Linux para fazer esta pergunta. Foi tudo tentativa e erro e navegação aleatória na Internet tarde da noite.

Mas as pessoas não devem precisar de 10 anos para isso. Se eu estivesse iniciando no Linux, gostaria de saber: Quando usar o pseudônimo, quando escrever e quando escrever uma função?

No caso de aliases, eu uso aliases para operações muito simples que não recebem argumentos.

alias houston='cd /home/username/.scripts/'

Isso parece óbvio. Mas algumas pessoas fazem isso:

alias command="bash bashscriptname"

(e adicione-o ao .bashrcarquivo)

Existe uma boa razão para fazer isso? Estou me esforçando muito, mas realmente não consigo pensar em nenhuma circunstância em que eu queira fazer isso. Portanto, se houver um caso extremo em que isso faça diferença, responda abaixo.

Porque é aí que eu colocaria algo no meu PATH e chmod +xisso, que é outra coisa que veio depois de anos de tentativa e erro do Linux.

O que me leva ao próximo tópico. Por exemplo, adicionei uma pasta oculta ( .scripts/) no diretório inicial ao meu PATH, apenas adicionando uma linha ao meu .bashrc( PATH=$PATH:/home/username/.scripts/), para que qualquer coisa executável lá seja preenchida automaticamente automaticamente.

Se eu precisasse.

Eu realmente não preciso disso, preciso? Eu usaria isso apenas para linguagens que não são o shell, como Python.

Se for o shell, posso escrever uma função dentro da mesma .bashrc:

funcname () {
  somecommand -someARGS "$@"
}

Como afirmei, descobri muito disso por tentativa e erro. E só vi verdadeiramente a beleza das funções quando meu computador morreu e fui forçado a usar os computadores das pessoas ao meu redor quando não as estavam usando.

Em vez de mover um diretório inteiro de scripts de um computador para outro, acabei substituindo o .bashrc de todo mundo pelo meu, pois eles nunca fizeram uma única modificação.

Mas eu perdi alguma coisa?

Então, o que você diria a um usuário iniciante do Linux sobre quando criar um alias, quando criar um script e quando escrever uma função?

Se não for óbvio, estou assumindo que as pessoas que responderem a isso farão uso das três opções. Se você usar apenas aliases, ou apenas scripts, ou apenas funções - ou se você usar apenas aliases e scripts ou aliases e funções ou scripts e funções - , essa pergunta não será realmente direcionada a você.

ixtmixilix
fonte
5
possível duplicata de funções festança vs roteiros
Gilles
10
+1 para declarar explicitamente todos os subconjuntos de {alias, script, função} aos quais esta pergunta não se destina. +1 pela fé infantil de que não há problema em você omitir o subconjunto nulo.
precisa saber é o seguinte
1
Embora a pergunta feita especificamente sobre o bash, observe que os cascos Bourne mais antigos tinham 'alias', mas não funções. Isso pode fazer a diferença se você estiver preocupado com a compatibilidade.
AndyB 4/16
Se .bashrc é realmente o melhor lugar, ou pelo menos um local sólido, para isso? Existem muitas maneiras de fazer a mesma coisa no Linux, que eu aprecio, no entanto, todas as coisas são iguais, eu prefiro fazer as coisas da maneira mais comum.
KIT10

Respostas:

242

Um alias efetivamente não deve (em geral) fazer mais do que alterar as opções padrão de um comando. Não passa de simples substituição de texto no nome do comando. Ele não pode fazer nada com argumentos, mas passá-los para o comando que ele realmente executa. Portanto, se você simplesmente precisar adicionar um argumento na frente de um único comando, um alias funcionará. Exemplos comuns são

# Make ls output in color by default.
alias ls="ls --color=auto"
# make mv ask before overwriting a file by default
alias mv="mv -i"

Uma função deve ser usada quando você precisar fazer algo mais complexo que um alias, mas que não seja útil por si só. Por exemplo, considere esta resposta em uma pergunta que eu fiz sobre grepo comportamento padrão da alteração , dependendo se ele está em um pipeline:

grep() { 
    if [[ -t 1 ]]; then 
        command grep -n "$@"
    else 
        command grep "$@"
    fi
}

É um exemplo perfeito de uma função porque é muito complexo para um alias (exigindo diferentes padrões com base em uma condição), mas não é algo que você precisará em um script não interativo.

Se você obtiver muitas funções ou funções muito grandes, coloque-as em arquivos separados em um diretório oculto e origine-as em ~/.bashrc:

if [ -d ~/.bash_functions ]; then
    for file in ~/.bash_functions/*; do
        . "$file"
    done
fi

Um script deve ser independente. Deve ter valor como algo que pode ser reutilizado ou usado para mais de uma finalidade.

Kevin
fonte
14
Também é importante lembrar que, a menos que seja fornecido com .ou, sourceum script é executado por um processo bash separado e possui seu próprio ambiente. Por esse motivo, qualquer coisa que modifique o ambiente do shell (por exemplo, funções, variáveis ​​etc.) não persistirá no ambiente do shell a partir do qual você executa o script.
Will Vousden
260

As outras respostas fornecem algumas diretrizes gerais suaves baseadas no gosto pessoal, mas ignoram muitos fatos pertinentes que devem ser considerados ao decidir entre scripts, funções ou aliases.

Aliases e funções ¹

  • Todo o conteúdo de aliases e funções é armazenado na memória do shell.
  • Uma conseqüência natural disso é aliases e funções podem ser usadas pelo shell atual, e não por outros programas que você possa chamar do shell, como editores de texto, scripts ou mesmo instâncias filho do mesmo shell.
  • Aliases e funções são executados pelo shell atual, ou seja, são executados dentro e afetam o ambiente atual do shell.² Nenhum processo separado é necessário para executar um alias ou função.

Scripts

  • Os shells não mantêm scripts na memória. Em vez disso, os scripts são lidos nos arquivos em que são armazenados toda vez que são necessários. Se o script for encontrado por meio de uma $PATHpesquisa, muitos shells armazenam um hash do nome do caminho na memória para economizar tempo em $PATHpesquisas futuras , mas essa é a extensão da pegada de memória de um script quando não estiver em uso.
  • Os scripts podem ser chamados de mais maneiras do que as funções e aliases. Eles podem ser passados ​​como argumento para um intérprete, como sh scriptou invocados diretamente como um executável; nesse caso, o intérprete na linha shebang (por exemplo #!/bin/sh) é invocado para executá-lo. Nos dois casos, o script é executado por um processo de intérprete separado, com seu próprio ambiente separado daquele do seu shell, cujo ambiente o script não pode afetar de forma alguma. De fato, o shell do intérprete nem precisa corresponder ao shell de chamada. Como os scripts chamados dessa maneira parecem se comportar como qualquer executável comum, eles podem ser usados ​​por qualquer programa.

    Finalmente, um script pode ser lido e executado pelo shell atual com ., ou em alguns shells source. Nesse caso, o script se comporta como uma função que é lida por demanda, em vez de ser constantemente mantida na memória.

Inscrição

Diante do exposto, podemos apresentar algumas diretrizes gerais para tornar algo um script ou função / alias.

  • Outros programas além do seu shell precisam ser capazes de usá-lo? Se assim for, tem que ser um script.

  • Deseja que ele esteja disponível apenas a partir de um shell interativo? É comum querer alterar o comportamento padrão de muitos comandos quando executados de maneira interativa sem afetar comandos / scripts externos. Nesse caso, use um alias / function definido no arquivo rc "somente no modo interativo" do shell (pois bashé isso .bashrc).

  • Precisa mudar o ambiente do shell? Uma função / alias ou um script de origem são escolhas possíveis.

  • É algo que você usa com frequência? Provavelmente é mais eficiente mantê-lo na memória; portanto, torne-o uma função / alias, se possível.

  • Por outro lado, é algo que você usa apenas raramente? Nesse caso, não faz sentido armazená-la quando você não precisar, então faça disso um script.


Functions Embora funções e aliases tenham algumas diferenças importantes, eles são agrupados porque as funções podem fazer tudo o que é alias. Os aliases não podem ter variáveis ​​locais, nem podem processar argumentos e são inconvenientes por qualquer coisa além de uma linha.

² Todo processo em execução em um sistema Unix possui um ambiente que consiste em vários variable=valuepares que geralmente contêm definições de configuração global, como LANGa localidade padrão e PATHa especificação do caminho de pesquisa executável.

jw013
fonte
26
IMHO Esta é a melhor resposta.
Luc M
4
O formato de pergunta / resposta é uma ótima idéia. Eu posso roubar isso. ;-)
Mikel
4
Vale a pena notar: se dois (ou mais) scripts precisam compartilhar algum código, provavelmente é melhor colocar esse código em uma função que está localizada em um terceiro arquivo que os dois scripts importam / fonte.
Kbolino 21/09
3
Outro item a ser adicionado a essa lista de perguntas: Você precisa alterar a funcionalidade no comando rapidamente? as alterações em um script serão refletidas em todas as sessões, enquanto as funções e aliases devem ser recarregados ou redefinidos por sessão.
Stratus3D
3
Boa resposta. Mais uma coisa importante (para mim): ao criar um "atalho" para outros utilitários, é melhor usar um alias porque o preenchimento automático existente funcionará apenas com alias, mas não com scripts ou funções (então +1 para alias). por exemplo, a criação de um alias g='gradle'eu recebo autocompletion Gradle quando usando o meu gapelido, mas não vai conseguir out-of-the-box ao usar um script com gradle $*ou uma função comgradle $@
Yoav Aharoni
37

Eu acho que depende do gosto de cada pessoa. Para mim, a lógica é assim:

  • Primeiro tento criar um alias, porque é o mais simples.
  • Se a coisa é muito complicada para caber em uma linha, tento torná-la uma função.
  • Quando a função começa a crescer além de uma dúzia de linhas, coloco-a em um script.

Não há realmente nada para impedi-lo de fazer algo que funcione .

phunehehe
fonte
6
Frequentemente pulo a opção de função e faço um script imediatamente. Mas concordo que é em parte uma questão de gosto
Bernhard
2
Uma função começa a fazer sentido se você precisar dela em vários scripts.
Nils
7
... ou se você precisar dos efeitos colaterais para alterar o shell atual .
glenn jackman
faz sentido, cara!
explorer
15

Pelo menos parcialmente, é uma questão de gosto pessoal. Por outro lado, existem algumas distinções funcionais claras:

  • aliases: adequado apenas para substituições simples de texto, sem argumentos / parâmetros
  • funções: fácil de escrever / usar, capacidade completa de script de shell, disponível apenas dentro do bash
  • scripts: mais ou menos como funções, mas também disponíveis (solicitáveis) fora do bash

Olhando para scripts de shell que fiz nos últimos anos, parei mais ou menos de escrever aliases (porque todos tendem a crescer em funções ao longo do tempo) e só crio scripts se precisar estar disponível também em ambientes que não sejam do bash.

PS: Quanto a alias command="bash bashscriptname", na verdade, não vejo motivo para fazer isso. Mesmo que bashscriptnamenão esteja em $ PATH, um simples alias c=/path/to/scriptseria suficiente.

nohillside
fonte
1
No alias command="bash bashscriptname"script não precisa necessariamente ser executável; no alias c=/path/to/scriptque tem que.
Martin - マ 'チ ン
Não é verdade que as funções estejam "disponíveis apenas no Bash". Se você quer dizer que eles são um recurso exclusivo do Bash, isso é simplesmente falso (shell Bourne e todos os derivativos compatíveis os possuem); e se você quiser dizer que eles são um recurso de shells interativos, isso também não é preciso (embora aliases, variáveis ​​e funções definidas em um arquivo carregado na inicialização por shells interativos obviamente não sejam carregados por shells não interativos).
Tripleee
@tripleee O significado era mais parecido com "você não pode exec()descascar funções" :-)
nohillside 17/09/18
11

Aqui estão alguns pontos adicionais sobre aliases e funções:

  • Alias ​​e função com o mesmo nome podem coexistir
  • o espaço para nome alternativo é procurado primeiro (veja o primeiro exemplo)
  • aliases não podem ser (des) definidos em subcascas ou ambiente não interativo (consulte o segundo exemplo)

Por exemplo:

alias f='echo Alias'; f             # prints "Alias"
function f { echo 'Function'; }; f  # prints "Alias"
unalias f; f                        # prints "Function"

Como podemos ver, existem espaços de nomes separados para aliases e funções; mais detalhes podem ser encontrados usando declare -A -p BASH_ALIASESe declare -f f, que imprime suas definições (ambas são armazenadas na memória).

Exemplo mostrando limitações de aliases:

alias a='echo Alias'
a        # OK: prints "Alias"
eval a;  # OK: prints "Alias"
( alias a="Nested"; a );  # prints "Alias" (not "Nested")
( unalias a; a );         # prints "Alias"
bash -c "alias aa='Another Alias'; aa"  # ERROR: bash: aa: command not found

Como podemos ver, os aliases não são aninhados, ao contrário das funções. Além disso, seu uso é limitado às sessões interativas.

Por fim, observe que você pode ter computação arbitrária em um alias declarando uma função chamando-a imediatamente, assim:

alias a_complex_thing='f() { do_stuff_in_function; } f'

que já é amplamente utilizado no caso de aliases do Git. O benefício de fazer isso ao declarar uma função é que seu alias não pode ser simplesmente substituído pela origem (ou uso .) de um script que declara uma função com o mesmo nome.

Leden
fonte
10

Quando escrever um script ...

  • Os scripts montam componentes de software (também conhecidos como ferramentas, comandos, processos, executáveis, programas) em componentes mais complexos, que podem ser montados em componentes ainda mais complexos.
  • Os scripts geralmente são executáveis ​​para que possam ser chamados pelo nome. Quando chamado, um novo subprocesso é gerado para a execução do script. Cópias de quaisquer exportvariáveis ​​e / ou funções ed são passadas por valor ao script. As alterações nessas variáveis não se propagam de volta para o script pai.
  • Os scripts também podem ser carregados (originados) como se fizessem parte do script de chamada. Isso é análogo ao que alguns outros idiomas chamam de "importação" ou "inclusão". Quando originados, eles são executados dentro do processo existente. Nenhum subprocesso é gerado.

Quando escrever uma função ...

  • Funções são scripts shell efetivamente pré-carregados. Eles têm um desempenho um pouco melhor do que chamar um script separado, mas apenas se for necessário ler a partir do disco mecânico. Atualmente, a proliferação de drives flash, SSDs e o cache normal do Linux na RAM não utilizada tornam essa melhoria praticamente imensurável.
  • As funções servem como o principal princípio do bash para obter modularidade, encapsulamento e reutilização. Eles melhoram a clareza, a confiabilidade e a capacidade de manutenção dos scripts.
  • As regras de sintaxe para chamar uma função são idênticas às de chamar um executável. Uma função com o mesmo nome de um executável seria invocada em vez do executável.
  • As funções são locais para o script em que estão.
  • As funções podem ser exportadas ( copiadas por valor ) para que possam ser usadas dentro de scripts chamados. Assim, as funções se propagam apenas para processos filhos, nunca para pais.
  • As funções criam comandos reutilizáveis ​​que geralmente são montados em bibliotecas (um script com apenas definições de funções) para serem originados por outros scripts.

Quando escrever um pseudônimo ...

Em scripts como scripts de biblioteca, algumas vezes é necessário um alias para uma função, como quando uma função é renomeada, mas é necessária compatibilidade com versões anteriores. Isso pode ser conseguido criando uma função simples com o nome antigo que passa todos os seus argumentos para a nova função ...

# A bash in-script 'alias'
function oldFunction () { newFunction "$@"; }
DocSalvager
fonte
9

Outra coisa que não acredito ter sido levantada: uma função é executada no contexto do processo de chamada, enquanto um script bifurca um novo shell.

Isso pode ser importante para o desempenho - uma função é mais rápida, pois não fork()e exec(). Em circunstâncias normais, a diferença é trivial, mas se você estiver depurando um sistema que está com memória insuficiente e com thrashing de página, isso pode fazer uma grande diferença.

Além disso, se você quiser modificar seu ambiente de shell atual, use uma função Por exemplo, uma função pode alterar a pesquisa de comando $PATHdo shell atual, mas um script não pode, porque opera em uma cópia fork / exec de $PATH.

Jan Steinman
fonte
Como essa propagação de funções para crianças funciona?
HappyFace
1
@HappyFace No Bash, você pode executar export -fuma função, embora o funcionamento interno preciso disso seja um pouco obscuro. Isso não é portátil para o shell Bourne tradicional, acredito.
tripleee
7

Script e alias e script e função não são mutuamente exclusivos. Você pode armazenar aliases e funções em scripts.

Scripts são apenas códigos que se tornam persistentes . Funções úteis e aliases que você deseja usar no futuro são armazenados em scripts. No entanto, um script geralmente é uma coleção de mais de uma função.

Como os aliases não são parametrizados , eles são muito limitados; geralmente para definir alguns parâmetros padrão.

Uma função é uma unidade de código separada , um conceito bem definido de algumas linhas de código que não podem ser separadas em partes menores e úteis; um que pode ser reutilizado diretamente ou outro por outras funções.

Usuário desconhecido
fonte
5

Se for muito rápido, torne-o um alias ou uma função.

Se for utilizável fora do seu shell preferido, torne-o um script. 1 1

Se receber argumentos, torne-o uma função ou um script.

Se precisar conter caracteres especiais, torne-o um alias ou um script. 2

Se precisar trabalhar com o sudo, torne-o um alias ou um script. 3

Se você deseja alterá-lo facilmente sem fazer logoff e logon, um script é mais fácil. 4

Notas de rodapé

1 Ou crie um alias, coloque-o ~/.enve defina-o export ENV="$HOME/.env", mas é complicado fazê-lo funcionar de maneira portável.

2 Os nomes das funções devem ser identificadores; portanto, devem começar com uma letra e podem conter apenas letras, dígitos e sublinhados. Por exemplo, eu tenho um alias alias +='pushd +1'. Não pode ser uma função.

3 E adicione o alias alias sudo='sudo '. O mesmo deve ser atribuído a qualquer outro comando, como strace, gdbetc., que aceita um comando como seu primeiro argumento.

4 Veja também: fpath. Claro que você também pode fazer source ~/.bashrcou algo parecido, mas isso geralmente tem outros efeitos colaterais.

Mikel
fonte
1
Eu não sabia que você poderia pseudônimo +no bash. Curiosamente, após o teste, descobri que no bash você pode criar +um alias, mas não uma função, como você diz, mas o zsh é o inverso - +pode ser uma função, mas não um alias.
Kevin
Em zshvocê tem que escrever alias -- +='some command here'.
Mikel
De alguma forma, eu não acho que aliasing +é portátil. Veja a especificação POSIX sobre nomes de alias
jw013 23/08/2012
3
Voto a favor para cobrir o sudouso. Em relação à nota de rodapé 4, eu armazeno meus apelidos ~/.bash_aliasese definições de funções ~/.bash_functionspara que eu possa substituí- sourcelos facilmente (sem qualquer perigo de efeitos colaterais).
Anthony Geoghegan
3

Apenas para adicionar algumas notas:

  • Somente scripts separados podem ser usados ​​com o sudo (como se você precisar editar um arquivo do sistema), por exemplo:
sudo v /etc/rc.conf  #where v runs vim in a new terminal window;
  • Somente aliases ou funções poderiam substituir os comandos do sistema com o mesmo nome (supondo que você adicione o diretório de scripts ao final do PATH, o que eu acho aconselhável para segurança em caso de criação acidental ou malévola de script com nome idêntico a um comando do sistema), por exemplo:
alias ls='ls --color=auto'  #enable colored output;
  • Aliases e funções levam menos memória e tempo para execução, mas levam tempo para carregar (já que o shell precisa interpretá-los todos antes de mostrar o prompt). Leve isso em consideração se você executar novos processos shell regularmente, por exemplo:
# pressing key to open new terminal
# waiting for a few seconds before shell prompt finally appears.

Fora isso, você pode usar a forma mais simples possível, ou seja, primeiro considere o alias, depois a função e o script.

corvinus
fonte
5
Aliases também podem ser usados ​​com sudo. Mas primeiro você precisa alias sudo='sudo '.
Mikel
Embora seja verdade que a execução de um script consuma momentaneamente mais memória durante a duração do fork + exec, ter muito código carregado na memória da instância atual do shell faz com que consuma mais memória daqui para frente, geralmente para armazenar código que apenas é usado muito raramente.
Tripleee
2

Minha regra de ouro é:

  • aliases - um comando, sem parâmetros
  • funções - um comando alguns parâmetros
  • script - vários comandos, sem parâmetros
Michael Durrant
fonte
1

Em um ambiente multiusuário (ou multi sysamin), eu uso scripts para tudo, mesmo que acabe sendo um invólucro curto de 'exec something ....'.

Claro, é tecnicamente mais lento / menos eficiente do que um alias ou função, mas isso quase nunca importa - e desde que esteja no caminho, um script sempre funciona.

Seu recurso pode ser chamado do cron, de algo com um ambiente reduzido ou modificado, como sudo ou env, ou o usuário pode estar apenas usando um shell diferente para você - todos ou que provavelmente quebrarão um alias ou função.

Se você tiver algo sensível ao desempenho, lide com isso como um caso especial ou, melhor ainda, considere isso um gatilho para reescrever em uma linguagem de script mais funcional.

Se estivermos falando sobre recursos que serão usados ​​apenas em outros scripts, considere também definir um shell padrão e escrever um script da biblioteca de funções que possa ser apenas. originado I para todos os outros scripts.

T

tjb63
fonte
0

Um exemplo para uma situação em que você provavelmente gostaria de usar um alias.

Sei que este é um post antigo, mas gostaria de apontar uma situação em que quase precisei usar uma combinação de alias com um script e optei por não usar uma função.

Eu tenho um script ~/.bin/chamado setupque faz o seguinte: 1

  1. me leva a um determinado diretório.
  2. define algumas variáveis.
  3. imprime mensagens sobre o estado do diretório. 2

O ponto é que, se eu apenas executasse setup <project-name>, não teria essas variáveis ​​definidas e nem teria chegado ao diretório. A solução que eu encontrei para ser o melhor foi adicionar este script para PATHe adicionar alias setup=". ~/.bin/setup"a ~/.bashrcou qualquer outra coisa.

Notas:

  1. Usei um script para esta tarefa e não uma função, não porque é particularmente longo, mas porque posso editá-lo e não preciso originar o arquivo após uma edição, se quiser atualizar o uso.
  2. Um caso semelhante me chamou atenção quando criei um script para recarregar todos os meus arquivos de ponto. 3
  1. O script está disponível no meu repositório dotfiles em .bin/.
  2. Sobre o script : dou a esse script um argumento que é um nome para o projeto que defini em avançado. Posteriormente, o script sabe trazer para mim o diretório certo, de acordo com um determinado csvarquivo. As variáveis ​​que ele define são obtidas do makefile nesse diretório. O script é executado depois ls -le git statuspara me mostrar o que está acontecendo lá.
  3. Esse script também está disponível no meu repositório dotfiles .bin/também.
Doron Behar
fonte
1
Hum, parece que deveria ser apenas uma função, não uma combinação de alias-script. (BTW, os ambientes são escritos "ambientes" e não "ambientes".)
Curinga
Obrigado por comentar sobre o erro de digitação, vou corrigi-lo no próximo commit. Quanto ao uso de uma função em vez de um script - talvez eu use uma função para esta tarefa específica e elimine esses aliases. O ponto é que, às vezes, é muito fácil usar um script e um alias se você editar esse script periodicamente .
Doron Behar
0

Quando escrever um script

Quando você desejar executar o comando a partir de uma ferramenta que não seja o shell.

Isso inclui o vim (para mim): tendo escrito filtros e outros programas como scripts, posso fazer algo como :%!my-filterfiltrar um arquivo através do programa do meu editor.

Se my-filterfosse uma função ou um alias, isso não seria possível.

D. Ben Knoble
fonte