Criar um alias do Bash que aceite um parâmetro?


1273

Eu costumava usar o CShell (), que permite criar um alias que aceita um parâmetro. A notação era algo como

alias junk="mv \\!* ~/.Trash"

No Bash, isso parece não funcionar. Dado que o Bash tem uma infinidade de recursos úteis, eu assumiria que este foi implementado, mas estou me perguntando como.



2
Certifique-se de usar aspas em torno dos argumentos"$1"
Christophe Roussy

Esta pergunta é fora de tópico para SO. Foi respondido em UNIX.SE , ea resposta é que você não precisa nem se preocupar: "Por exemplo, se você fosse apelido lspara ls -la, em seguida, digitando ls foo barseria realmente executar ls -la foo bar. Na linha de comando"
Dan Dascalescu

7
Isso não ajuda com interpolando uma variável no meio de uma corda
Franz Sittampalam

1
Aqui está o teste lil apelido que eu usei para descobrir esta falácia ... alias test_args="echo PREFIX --$1-- SUFFIX", que quando chamado com test_args ABCDrendimentos a seguinte saída do consolePREFIX ---- SUFFIX ABCD
jxramos

Respostas:


2126

O alias do bash não aceita parâmetros diretamente. Você terá que criar uma função.

aliasnão aceita parâmetros, mas uma função pode ser chamada como um alias. Por exemplo:

myfunction() {
    #do things with parameters like $1 such as
    mv "$1" "$1.bak"
    cp "$2" "$1"
}


myfunction old.conf new.conf #calls `myfunction`

A propósito, as funções do Bash definidas no seu .bashrce em outros arquivos estão disponíveis como comandos no seu shell. Por exemplo, você pode chamar a função anterior como esta

$ myfunction original.conf my.conf

48
Devo colocar $1aspas?
Jürgen Paul

263
Você nem precisa declarar o alias. Apenas definir a função serve.
Din10 Dez13

207
Se você estiver alterando um alias para uma função, o sourceseu .bashrc adicionará a função, mas não anulará o antigo alias. Como os aliases são precedentes mais altos que as funções, ele tentará usar o alias. Você precisa fechar e reabrir seu shell, ou então chamar unalias <name>. Talvez eu salve alguém nos 5 minutos que desperdicei.
Marty Neal

56
@MartinNeal: Um truque de economia de tempo que aprendi na Sun é fazer um exec bash: Ele iniciará um novo shell, fornecendo uma leitura limpa de suas configurações, como se você fechasse e reabrisse, mas mantendo também as configurações variáveis ​​de ambiente da sessão . Além disso, executar bashsem o exec pode ser útil quando você deseja manipular os pensamentos como uma pilha.
Macneil Shonle

21
@ mich - sim, sempre coloque variáveis ​​bash entre aspas. por exemplo mv "$1" "$1.bak". sem aspas, se $ 1 fosse "olá mundo", você executaria em mv hello world hello world.bakvez de mv "hello world" "hello world.bak".
Orion elenzil

208

Refinando a resposta acima, você pode obter uma sintaxe de 1 linha, como é possível para aliases, o que é mais conveniente para definições ad-hoc em arquivos shell ou .bashrc:

bash$ myfunction() { mv "$1" "$1.bak" && cp -i "$2" "$1"; }

bash$ myfunction original.conf my.conf

Não se esqueça do ponto e vírgula antes do colchete direito de fechamento. Da mesma forma, para a pergunta real:

csh% alias junk="mv \\!* ~/.Trash"

bash$ junk() { mv "$@" ~/.Trash/; }

Ou:

bash$ junk() { for item in "$@" ; do echo "Trashing: $item" ; mv "$item" ~/.Trash/; done; }

3
Prefiro essa resposta, pois mostra a iteração na matriz de argumentos que podem aparecer. $ 1 e $ 2 são apenas casos especiais, que também são ilustrados nesta resposta.
Philo vivero 18/09/14

16
Mude mv "$1" "$1.bak"; cp "$2" "$1" para mv "$1" "$1.bak" && cp "$2" "$1"para não perder seus dados quando mvhouver problemas (por exemplo, sistema de arquivos cheio).
Henk Langeveld

3
"Não esqueça o ponto e vírgula antes do colchete direito de fechamento." Muitas vezes isso! Obrigado :)
Kaushal Modi

E os espaços após o primeiro suporte e antes do último suporte também são necessários.
perfil completo de Bryan Chapel

1
@HenkLangeveld Embora sua observação esteja perfeitamente correta, ela mostra que você já existe há algum tempo - eu não saberia quando encontrei pela última vez um erro de "nenhum espaço deixado no dispositivo" ;-). (Eu o uso regularmente, quando não encontro mais espaço no balcão da cozinha!) Parece que isso não acontece com muita frequência atualmente.
Peter - Restabelece Monica

124

A pergunta é simplesmente feita errada. Você não aliascria um alias que aceita parâmetros porque apenas adiciona um segundo nome para algo que já existe. A funcionalidade que o OP deseja é o functioncomando para criar uma nova função. Você não precisa aliasar a função, pois a função já tem um nome.

Eu acho que você quer algo assim:

function trash() { mv "$@" ~/.Trash; }

É isso aí! Você pode usar os parâmetros $ 1, $ 2, $ 3, etc, ou apenas encher todos eles com $ @


7
Esta resposta já diz tudo. Se eu tivesse lido no final desta página, economizaria algum tempo.
28416 joe

-1 Um alias e uma função não são equivalentes ...echo -e '#!/bin/bash\nshopt -s expand_aliases\nalias asrc='\''echo "${BASH_SOURCE[0]}"'\'' # note the '\''s\nfunction fsrc(){ echo "${BASH_SOURCE[0]}";}'>>file2&&echo -e '#!/bin/bash\n. file2\nalias rl='\''readlink -f'\''\nrl $(asrc)\nrl $(fsrc)'>>file1&&chmod +x file1&&./file1;rm -f file1 file2
Lógica Fuzzy

Você realmente deve citar $@para suportar nomes de arquivos com espaços, etc.
Tom Hale

Era para ser uma explicação geral de que você não tem parâmetros de alias, mas usa uma função. A função fornecida foi apenas um exemplo para contrariar o exemplo original. Eu não acho que a pessoa estava procurando por uma função de lixo de uso geral. No entanto, no interesse de apresentar um código melhor, atualizei a resposta.
Evan Langlois

1
@FuzzyLogic - passei alguns minutos tentando descompactar o código no seu comentário. É muito esforço (interessante) para empacotar algum código em um comentário onde você não pode formatá-lo adequadamente. Ainda não descobri exatamente o que ele faz ou, mais importante, por que ele suporta sua afirmação (correta).
Joe

110

TL; DR: faça isso em vez disso

É muito mais fácil e mais legível usar uma função do que um alias para colocar argumentos no meio de um comando.

$ wrap_args() { echo "before $@ after"; }
$ wrap_args 1 2 3
before 1 2 3 after

Se você continuar lendo, aprenderá coisas que não precisa saber sobre o processamento de argumentos de shell. O conhecimento é perigoso. Apenas obtenha o resultado que deseja, antes que o lado sombrio controle seu destino para sempre.

Esclarecimento

bashaliases fazer aceitar argumentos, mas apenas no final :

$ alias speak=echo
$ speak hello world
hello world

Colocar argumentos no meio do comando via aliasé realmente possível, mas fica feio.

Não tente fazer isso em casa, crianças!

Se você gosta de contornar as limitações e fazer o que os outros dizem ser impossível, aqui está a receita. Só não me culpe se seu cabelo estiver desgrenhado e seu rosto acabar coberto de fuligem ao estilo de cientista louco.

A solução alternativa é passar os argumentos que aliasaceitam apenas no final para um wrapper que os insira no meio e execute seu comando.

Solução 1

Se você é realmente contra usar uma função em si, você pode usar:

$ alias wrap_args='f(){ echo before "$@" after;  unset -f f; }; f'
$ wrap_args x y z
before x y z after

Você pode substituir $@por $1se desejar apenas o primeiro argumento.

Explicação 1

Isso cria uma função temporária f, à qual são passados ​​os argumentos (observe que fé chamado no final). Ele unset -fremove a definição da função, pois o alias é executado para que não fique por aí depois.

Solução 2

Você também pode usar um subshell:

$ alias wrap_args='sh -c '\''echo before "$@" after'\'' _'

Explicação 2

O alias cria um comando como:

sh -c 'echo before "$@" after' _

Comentários:

  • O espaço reservado _é obrigatório, mas pode ser qualquer coisa. Ele é definido como sh's' $0e é necessário para que o primeiro dos argumentos fornecidos pelo usuário não seja consumido. Demonstração:

    sh -c 'echo Consumed: "$0" Printing: "$@"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
    
  • As aspas simples dentro das aspas simples são necessárias. Aqui está um exemplo de que não funciona com aspas duplas:

    $ sh -c "echo Consumed: $0 Printing: $@" alcohol drunken babble
    Consumed: -bash Printing:
    

    Aqui, os valores dos shell interativos $0e $@são substituídos pelo duplo entre aspas antes de serem passados ​​para sh. Aqui está a prova:

    echo "Consumed: $0 Printing: $@"
    Consumed: -bash Printing:
    

    As aspas simples garantem que essas variáveis ​​não sejam interpretadas pelo shell interativo e sejam transmitidas literalmente para sh -c.

    Você pode usar aspas duplas e \$@, mas a melhor prática é citar seus argumentos (pois eles podem conter espaços) e \"\$@\"parecer ainda mais feio, mas pode ajudá-lo a vencer um concurso de ofuscação em que cabelos desgrenhados são um pré-requisito para entrada.


2
para solução 1, certifique-se de usar aspas simples, e evitar \" em torno dos US $ @ A técnica muito útil se você precisar dele ty...
sgtz

A solução 1 funcionou para mim, envolva o rdesktop-vrdp com o argumento de cada servidor que eu quero.
Hsehdar

Aceitar argumentos no final é exatamente o que eu precisava para substituir "git checkout {branchname}" por um simples "gc {branchname}". Em Bash_profile eu simplesmente tinha que adicionaralias gc='git checkout'
AbstractVoid

@sgtz Por que você deseja remover aspas $@? Eles são necessários se você tiver, por exemplo, arquivos com espaços neles.
Tom Hale

@ TomHale Isso é um pouco de linguística, mas a solução 1 não é realmente útil se alguém realmente é contra o uso de uma função (qualquer que seja a razão), uma vez que você logo após se admite que está apenas mudando a definição de uma função para o tempo de execução de uma função. o apelido. segundo, "aliases do bash aceitam argumentos, mas apenas no final" é mais semântica: os alias são substituições por definição de palavras por strings. eles não precisam aceitar argumentos; os comandos que são executados via substituição, no entanto, podem. alias é uma substituição de personagem, nada mais, nada menos. não?
BUFU

39

Uma solução alternativa é usar o marcador , uma ferramenta que eu criei recentemente que permite "marcar" modelos de comando e colocar facilmente o cursor nos espaços reservados para comandos:

marcador de linha de comando

Descobri que, na maioria das vezes, estou usando funções de shell para não precisar escrever comandos usados ​​com freqüência na linha de comando. A questão do uso de funções para este caso de uso é adicionar novos termos ao meu vocabulário de comando e ter que lembrar quais parâmetros de funções se referem no comando real. O objetivo do marcador é eliminar esse fardo mental.


14

Tudo que você precisa fazer é criar uma função dentro de um alias:

$ alias mkcd='_mkcd(){ mkdir "$1"; cd "$1";}; _mkcd'

Você deve colocar aspas duplas em torno de "$ 1", pois aspas simples não funcionarão.


5
Isso é inteligente, mas, além do fato de que a pergunta especificou para criar um alias, existe alguma razão para não apenas criar a função com o mesmo nome que o alias inicialmente?
Xaxxon

1
@xaxxon Na verdade não, mas é a maneira mais fácil de usar um apelido, não uma função.
Ken Tran

1
Isso não funciona. No Ubuntu 18, estou recebendo erro bash: syntax error near unexpected token '{mkdir'.
Shital Shah

faltando um rastreio ;dentro da função, _mkcdprovavelmente o erro é disso.
Jetchisel 22/01

Deixe espaço antes e depois da {e}
hesham_EE 22/01

10

Aqui estão três exemplos de funções que tenho no meu ~/.bashrc, que são essencialmente aliases que aceitam um parâmetro:

#Utility required by all below functions.
#/programming/369758/how-to-trim-whitespace-from-bash-variable#comment21953456_3232433
alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'"

.

:<<COMMENT
    Alias function for recursive deletion, with are-you-sure prompt.

    Example:
        srf /home/myusername/django_files/rest_tutorial/rest_venv/

    Parameter is required, and must be at least one non-whitespace character.

    Short description: Stored in SRF_DESC

    With the following setting, this is *not* added to the history:
        export HISTIGNORE="*rm -r*:srf *"
    - https://superuser.com/questions/232885/can-you-share-wisdom-on-using-histignore-in-bash

    See:
    - y/n prompt: https://stackoverflow.com/a/3232082/2736496
    - Alias w/param: https://stackoverflow.com/a/7131683/2736496
COMMENT
#SRF_DESC: For "aliaf" command (with an 'f'). Must end with a newline.
SRF_DESC="srf [path]: Recursive deletion, with y/n prompt\n"
srf()  {
    #Exit if no parameter is provided (if it's the empty string)
        param=$(echo "$1" | trim)
        echo "$param"
        if [ -z "$param" ]  #http://tldp.org/LDP/abs/html/comparison-ops.html
        then
          echo "Required parameter missing. Cancelled"; return
        fi

    #Actual line-breaks required in order to expand the variable.
    #- https://stackoverflow.com/a/4296147/2736496
    read -r -p "About to
    sudo rm -rf \"$param\"
Are you sure? [y/N] " response
    response=${response,,}    # tolower
    if [[ $response =~ ^(yes|y)$ ]]
    then
        sudo rm -rf "$param"
    else
        echo "Cancelled."
    fi
}

.

:<<COMMENT
    Delete item from history based on its line number. No prompt.

    Short description: Stored in HX_DESC

    Examples
        hx 112
        hx 3

    See:
    - https://unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string
COMMENT
#HX_DESC: For "aliaf" command (with an 'f'). Must end with a newline.
HX_DESC="hx [linenum]: Delete history item at line number\n"
hx()  {
    history -d "$1"
}

.

:<<COMMENT
    Deletes all lines from the history that match a search string, with a
    prompt. The history file is then reloaded into memory.

    Short description: Stored in HXF_DESC

    Examples
        hxf "rm -rf"
        hxf ^source

    Parameter is required, and must be at least one non-whitespace character.

    With the following setting, this is *not* added to the history:
        export HISTIGNORE="*hxf *"
    - https://superuser.com/questions/232885/can-you-share-wisdom-on-using-histignore-in-bash

    See:
    - https://unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string
COMMENT
#HXF_DESC: For "aliaf" command (with an 'f'). Must end with a newline.
HXF_DESC="hxf [searchterm]: Delete all history items matching search term, with y/n prompt\n"
hxf()  {
    #Exit if no parameter is provided (if it's the empty string)
        param=$(echo "$1" | trim)
        echo "$param"
        if [ -z "$param" ]  #http://tldp.org/LDP/abs/html/comparison-ops.html
        then
          echo "Required parameter missing. Cancelled"; return
        fi

    read -r -p "About to delete all items from history that match \"$param\". Are you sure? [y/N] " response
    response=${response,,}    # tolower
    if [[ $response =~ ^(yes|y)$ ]]
    then
        #Delete all matched items from the file, and duplicate it to a temp
        #location.
        grep -v "$param" "$HISTFILE" > /tmp/history

        #Clear all items in the current sessions history (in memory). This
        #empties out $HISTFILE.
        history -c

        #Overwrite the actual history file with the temp one.
        mv /tmp/history "$HISTFILE"

        #Now reload it.
        history -r "$HISTFILE"     #Alternative: exec bash
    else
        echo "Cancelled."
    fi
}

Referências:


Hã? Isso é uma função, não um apelido. E uma de suas referências é essa mesma pergunta.
Tripleee

2
Não, funciona exatamente como uma função. Como várias das respostas aqui explicam, você não pode criar um alias que aceita um parâmetro. O que você pode fazer é escrever uma função, que é o que você fez.
Tripleee

1
@tripleee Do meu ponto de vista para iniciantes, antes de ler seu comentário, pensei que era um apelido (uma maneira alternativa de criar um). Funciona exatamente como um, até onde eu sei. É essencialmente um alias que aceita um parâmetro, mesmo se eu estiver de fora da terminologia. Eu não vejo o problema. Esclareci o link para a outra resposta neste problema. Isso me ajudou a criar isso.
precisa saber é o seguinte

1
Talvez isso não responda especificamente à questão de "um alias do bash que aceite um parâmetro", porque agora entendo que isso não é possível, mas certamente o responde com eficácia . O que estou perdendo aqui?
Alreralmind

2
Alguns exemplos realmente bons aqui e um código bem comentado. Uma grande ajuda para as pessoas que vêm a esta página.
chim 25/03

9

O alias do Bash absolutamente aceita parâmetros. Acabei de adicionar um alias para criar um novo aplicativo de reação que aceita o nome do aplicativo como parâmetro. Aqui está o meu processo:

Abra o bash_profile para editar no nano

nano /.bash_profile

Adicione seus aliases, um por linha:

alias gita='git add .'
alias gitc='git commit -m "$@"'
alias gitpom='git push origin master'
alias creact='npx create-react-app "$@"'

nota: o "$ @" aceita parâmetros passados ​​como "creact my-new-app"

Salvar e sair do nano editor

ctrl + o para escrever (pressione enter); ctrl + x para sair

Diga ao terminal para usar os novos aliases em .bash_profile

source /.bash_profile

É isso aí! Agora você pode usar seus novos aliases


6

NB: Caso a idéia não seja óbvia, é uma má idéia usar aliases para qualquer coisa, exceto alias, sendo o primeiro a 'função em um alias' e o segundo o 'difícil redirecionar / fonte'. Além disso, existem falhas (que eu pensei que seriam óbvias, mas apenas no caso de você estar confuso: não pretendo que elas sejam realmente usadas ... em qualquer lugar!)

.................................................. .................................................. ............................................

Eu já respondi isso antes, e sempre foi assim no passado:

alias foo='__foo() { unset -f $0; echo "arg1 for foo=$1"; }; __foo()'

o que é bom e bom, a menos que você esteja evitando o uso de funções todos juntos. Nesse caso, você pode aproveitar a vasta capacidade do bash para redirecionar texto:

alias bar='cat <<< '\''echo arg1 for bar=$1'\'' | source /dev/stdin'

Ambos têm aproximadamente o mesmo comprimento, fornecem ou recebem alguns caracteres.

O verdadeiro kicker é a diferença de tempo, sendo a parte superior o 'método da função' e a parte inferior o método da 'fonte de redirecionamento'. Para provar essa teoria, o momento fala por si:

arg1 for foo=FOOVALUE
 real 0m0.011s user 0m0.004s sys 0m0.008s  # <--time spent in foo
 real 0m0.000s user 0m0.000s sys 0m0.000s  # <--time spent in bar
arg1 for bar=BARVALUE
ubuntu@localhost /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.010s user 0m0.004s sys 0m0.004s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE
ubuntu@localhost /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.011s user 0m0.000s sys 0m0.012s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE
ubuntu@localhost /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.012s user 0m0.004s sys 0m0.004s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE
ubuntu@localhost /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.010s user 0m0.008s sys 0m0.004s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE

Esta é a parte inferior de cerca de 200 resultados, feitos em intervalos aleatórios. Parece que a criação / destruição de funções leva mais tempo que o redirecionamento. Esperemos que isso ajude os futuros visitantes a esta pergunta (não queria mantê-la para mim).


2
Tendo um alias que define uma função, execute essa função apenas como bobo. Basta escrever uma função. Para quase todos os propósitos, os aliases são substituídos pelas funções do shell.
Geirha 20/05

caso você não tenha lido a coisa toda, o que você pode tentar, eu estava ilustrando por que usar o método de função não era tão bom em primeiro lugar e, além disso, não é nada bobo. Pare de postar e reter votos apenas porque não gostou pessoalmente ... ou pelo menos deu uma explicação válida. Xingamentos é o primeiro sinal perto mentalidade ...
osirisgothra

1
O segundo (o apelido de barra) tem vários efeitos colaterais. Primeiramente, o comando não pode usar stdin. Em segundo lugar, se nenhum argumento for fornecido após o alias, quaisquer que sejam os argumentos passados ​​pelo shell. O uso de uma função evita todos esses efeitos colaterais. (Também uso inútil de gato).
Geirha 20/05

2
ei, eu nunca disse que era uma boa ideia fazer isso, apenas disse que havia outras maneiras de fazê-lo - a idéia principal aqui é que você não deve usar funções em aliases porque elas são mais lentas do que complicadas. redirecionamento, eu pensei que teria sido óbvio, mas eu acho que tem que soletrar para todos (que eu também obter gritou para o cargo a ser muito inchado se eu fizesse isso, em primeiro lugar)
osirisgothra

1
Como os tempos da barra são todos os zero, eu suspeitaria que o shell não esteja cronometrando isso corretamente. Observe que a mensagem de horário é impressa antes da execução do comando? Portanto, ele não cronometra nada e depois executa a barra, para que você não esteja medindo a barra. Faça algo mais complexo, ou um grande loop no bar para que você fique mais óbvio que não está cronometrando nada
Evan Langlois

6

Se você estiver procurando uma maneira genérica de aplicar todos os parâmetros a uma função, não apenas uma ou duas ou outra quantidade codificada, você pode fazer isso desta maneira:

#!/usr/bin/env bash

# you would want to `source` this file, maybe in your .bash_profile?
function runjar_fn(){
    java -jar myjar.jar "$@";
}

alias runjar=runjar_fn;

Portanto, no exemplo acima, passo todos os parâmetros a partir de quando corro runjarpara o alias.

Por exemplo, se eu fizesse runjar hi thereisso acabaria realmente em execução java -jar myjar.jar hi there. Se eu fizesse runjar one two threeisso iria correr java -jar myjar.jar one two three.

Gosto desta $@solução porque funciona com qualquer número de parâmetros.


4
por que não apenas nomear a função em runjarvez de torná-la um alias para uma função? Parece desnecessariamente complicado!
Evan Langlois

As funções @EvanLanglois são automaticamente adicionadas alias(ou têm as propriedades das coisas passadas alias) nos arquivos bash? se sim, então eu simplesmente não sei disso #
Micah

Não entenda o que você quer dizer. Um apelido é outro nome, seja uma pessoa ou uma função. Então, você criou uma função e criou um segundo nome para a função. Não há nada de especial no alias, é apenas um segundo nome e não é obrigatório. O que você digita na linha de comando pode ser funções internas ou externas; portanto, "function" cria um novo comando, não um apelido.
Evan Langlois

4
Isso não tem sentido. É o mesmo que alias runjar='java -jar myjar.jar'. Os aliases aceitam argumentos, mas apenas no final.
Tom Hale

6

Respeitosamente para todos aqueles que dizem que você não pode inserir um parâmetro no meio de um alias, eu apenas testei e descobri que funcionava.

alias mycommand = "python3" $ 1 "script.py --folderoutput RESULTADOS /"

quando executei meu comando foobar, ele funcionou exatamente como se eu tivesse digitado o comando à mão.


Absolutamente, o alias pode obter parâmetros, eu tenho idades usando este, por exemplo: alias commit = "git commit -m $ 1"
agapitocandemor 28/03

Não funciona para mim: me alias myalias="echo 1 "$1" 3"; myalias 2dá:1 3 2
dirdi

4

Existem razões técnicas legítimas para querer uma solução generalizada para o problema de o alias do bash não ter um mecanismo para reposicionar argumentos arbitrários. Uma razão é se o comando que você deseja executar seria afetado negativamente pelas mudanças no ambiente resultantes da execução de uma função. Em todos os outros casos, as funções devem ser usadas.

O que recentemente me levou a tentar uma solução para isso é que eu queria criar alguns comandos abreviados para imprimir as definições de variáveis ​​e funções. Então, eu escrevi algumas funções para esse fim. No entanto, existem certas variáveis ​​que são (ou podem ser) alteradas por uma chamada de função. Entre eles estão:

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

O comando básico que eu estava usando (em uma função) para imprimir defns de variáveis. na saída do formulário pelo comando set foi:

sv () { set | grep --color=never -- "^$1=.*"; }

Por exemplo:

> V=voodoo
sv V
V=voodoo

Problema: Isso não imprimirá as definições das variáveis ​​mencionadas acima, como estão no contexto atual , por exemplo, se em um prompt de shell interativo (ou não em nenhuma chamada de função), FUNCNAME não estiver definido. Mas minha função me diz a informação errada:

> sv FUNCNAME
FUNCNAME=([0]="sv")

Uma solução que eu encontrei foi mencionada por outras pessoas em outros posts sobre esse tópico. Para que este comando específico imprima a variável defns., E que requer apenas um argumento, eu fiz o seguinte:

alias asv='(grep -- "^$(cat -)=.*" <(set)) <<<'

O que fornece a saída correta (nenhuma) e o status do resultado (falso):

> asv FUNCNAME
> echo $?
1

No entanto, ainda me sentia obrigado a encontrar uma solução que funcionasse para números arbitrários de argumentos.

Uma solução geral para passar argumentos arbitrários para um comando com alias do Bash:

# (I put this code in a file "alias-arg.sh"):

# cmd [arg1 ...] – an experimental command that optionally takes args,
# which are printed as "cmd(arg1 ...)"
#
# Also sets global variable "CMD_DONE" to "true".
#
cmd () { echo "cmd($@)"; declare -g CMD_DONE=true; }

# Now set up an alias "ac2" that passes to cmd two arguments placed
# after the alias, but passes them to cmd with their order reversed:
#
# ac2 cmd_arg2 cmd_arg1 – calls "cmd" as: "cmd cmd_arg1 cmd_arg2"
#
alias ac2='
    # Set up cmd to be execed after f() finishes:
    #
    trap '\''cmd "${CMD_ARGV[1]}" "${CMD_ARGV[0]}"'\'' SIGUSR1;
    #        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    #       (^This is the actually execed command^)
    #
    # f [arg0 arg1 ...] – acquires args and sets up trap to run cmd:
    f () {
        declare -ag CMD_ARGV=("$@");  # array to give args to cmd
        kill -SIGUSR1 $$;             # this causes cmd to be run
        trap SIGUSR1;                 # unset the trap for SIGUSR1
        unset CMD_ARGV;               # clean up env...
        unset f;                      # incl. this function!
    };
    f'  # Finally, exec f, which will receive the args following "ac2".

Por exemplo:

> . alias-arg.sh
> ac2 one two
cmd(two one)
>
> # Check to see that command run via trap affects this environment:
> asv CMD_DONE
CMD_DONE=true

Uma coisa boa dessa solução é que todos os truques especiais usados ​​para manipular parâmetros posicionais (argumentos) para comandos funcionarão ao compor o comando interceptado. A única diferença é que a sintaxe da matriz deve ser usada.

Por exemplo,

Se você deseja "$ @", use "$ {CMD_ARGV [@]}".

Se você quiser "$ #", use "$ {# CMD_ARGV [@]}".

Etc.


3

Uma vez eu fiz um projeto divertido e ainda o uso. Está mostrando alguma animação enquanto eu copio arquivos via cpcomando coz cp, não mostramos nada e é meio frustrante. Então eu fiz esse alias

alias cp="~/SCR/spiner cp"

E este é o script spiner

#!/bin/bash

#Set timer
T=$(date +%s)

#Add some color
. ~/SCR/color

#Animation sprites
sprite=( "(* )  ( *)" " (* )( *) " " ( *)(* ) " "( *)  (* )" "(* )  ( *)" )

#Print empty line and hide cursor
printf "\n${COF}"

#Exit function
function bye { printf "${CON}"; [ -e /proc/$pid ] && kill -9 $pid; exit; }; trap bye INT

#Run our command and get its pid
"$@" & pid=$!

#Waiting animation
i=0; while [ -e /proc/$pid ]; do sleep 0.1

    printf "\r${GRN}Please wait... ${YLW}${sprite[$i]}${DEF}"
    ((i++)); [[ $i = ${#sprite[@]} ]] && i=0

done

#Print time and exit
T=$(($(date +%s)-$T))
printf "\n\nTime taken: $(date -u -d @${T} +'%T')\n"

bye

É assim

insira a descrição da imagem aqui

Animação Ciclada)

insira a descrição da imagem aqui


2

Para obter parâmetros, você deve usar funções!

No entanto, $ @ é interpretado ao criar o alias em vez de durante a execução do alias e escapar do $ também não funciona. Como eu resolvo este problema?

Você precisa usar a função shell em vez de um alias para se livrar desse problema. Você pode definir foo da seguinte maneira:

function foo() { /path/to/command "$@" ;}

OU

foo() { /path/to/command "$@" ;}

Por fim, chame seu foo () usando a seguinte sintaxe:

foo arg1 arg2 argN

Certifique-se de adicionar seu foo () ~/.bash_profileou ~/.zshrcarquivo.

No seu caso, isso funcionará

function trash() { mv $@ ~/.Trash; }

Eu não entendo sua ideia. Eu acho que não é importante quando os argumentos são interpretados. Aliase aceita quantos parâmetros desejar no final.
Timo

Mas é melhor fazer isso com funções. Aliases não se destinam a isso.
Ahmad Awais

1

As funções são de fato quase sempre a resposta, já amplamente contribuída e confirmada por esta citação da página de manual: "Para quase todos os propósitos, os aliases são substituídos pelas funções de shell".

Para garantir a integridade e como isso pode ser útil (sintaxe ligeiramente mais leve), pode-se observar que, quando os parâmetros seguem o alias, eles ainda podem ser usados ​​(embora isso não atenda aos requisitos do OP). Provavelmente, isso é mais fácil de demonstrar com um exemplo:

alias ssh_disc='ssh -O stop'

permite digitar smth like ssh_disc myhost, que é expandido conforme o esperado:ssh -O stop myhost

Isso pode ser útil para comandos que usam argumentos complexos (minha memória não é mais o que costumava ser ...)


1

Ambas as funções e aliases podem usar parâmetros como outros mostraram aqui. Além disso, gostaria de destacar alguns outros aspectos:

1. função é executada em seu próprio escopo, alias compartilha escopo

Pode ser útil conhecer essa diferença nos casos em que você precisa ocultar ou expor algo. Também sugere que uma função é a melhor escolha para encapsulamento.

function tfunc(){
    GlobalFromFunc="Global From Func" # Function set global variable by default
    local FromFunc="onetwothree from func" # Set a local variable

}

alias talias='local LocalFromAlias="Local from Alias";  GlobalFromAlias="Global From Alias" # Cant hide a variable with local here '
# Test variables set by tfunc
tfunc # call tfunc
echo $GlobalFromFunc # This is visible
echo $LocalFromFunc # This is not visible
# Test variables set by talias
# call talias
talias
echo $GlobalFromAlias # This is invisible
echo $LocalFromAlias # This variable is unset and unusable 

Resultado:

bash-3.2$     # Test variables set by tfunc
bash-3.2$     tfunc # call tfunc
bash-3.2$     echo $GlobalFromFunc # This is visible
Global From Func
bash-3.2$     echo $LocalFromFunc # This is not visible

bash-3.2$     # Test variables set by talias
bash-3.2$     # call talias
bash-3.2$     talias
bash: local: can only be used in a function
bash-3.2$     echo $GlobalFromAlias # This is invisible
Global From Alias
bash-3.2$ echo $LocalFromAlias # This variable is unset and unusable

2. script wrapper é uma escolha melhor

Ocorreu-me várias vezes que um alias ou função não pode ser encontrado ao efetuar login viassh ou envolvendo a alternância de nomes de usuário ou ambiente multiusuário. Existem dicas e truques com arquivos de ponto de origem, ou um interessante com alias: alias sd='sudo 'permite que esse alias subseqüente alias install='sd apt-get install'funcione conforme o esperado (observe o espaço extra sd='sudo '). No entanto, um script de wrapper funciona melhor que uma função ou alias em casos como este. A principal vantagem de um script de wrapper é que ele é visível / executável para o caminho pretendido (por exemplo, / usr / loca / bin /) onde, como função / alias, precisa ser originado antes de ser utilizado. Por exemplo, você coloca uma função em ~ / .bash_profile ou ~ / .bashrc for bash, mas depois muda para outro shell (por exemplo,zsh), a função não está mais visível. Portanto, quando você está em dúvida, um script de wrapper é sempre a solução mais confiável e portátil.


1
Por que você acha que a versão da função não funciona?
Tripleee 28/07/19

@tripleee. Editei a postagem para torná-la mais legível. Sob o título "1. alias funciona, função ..." ilustrei por que uma versão de função não funciona. E talvez para responder à pergunta diretamente, a função introduz um novo escopo onde o alias não.
Biocyberman

Você diz que não funciona, mas eu não acredito em você. sourceem uma função funciona muito bem.
tripleee

1
f () { source /tmp/nst; }faz exatamente o que eu espero. Talvez você PATHesteja errado, então isso corre errado activateou algo assim; mas correr sourcede uma função funciona bem.
tripleee

1
BTW, re: usando a functionpalavra - chave em sua definição, consulte wiki.bash-hackers.org/scripting/obsolete - function funcname {é uma sintaxe antiga do ksh que o bash suporta para compatibilidade retroativa com shells pré-POSIX, enquanto funcname() {é a sintaxe oficial padronizada do POSIX. function funcname() {é um erro fatal dos dois que não é compatível com o ksh antigo ou compatível com o POSIX sh e, portanto, é melhor evitar.
Charles Duffy

1

Aqui está o exemplo:

alias gcommit='function _f() { git add -A; git commit -m "$1"; } ; _f'

Muito importante:

  1. Há um espaço depois {e antes }.
  2. Há um ;após cada comando em sequência. Se você esquecer isso após o último comando, verá um >prompt!
  3. O argumento está entre aspas como "$1"

2
Não há necessidade de criar um alias para uma função, basta usar a função diretamente.
user3439894 22/01

1

Como já foi apontado por outros, o uso de uma função deve ser considerado uma prática recomendada.

No entanto, aqui está outra abordagem, aproveitando xargs:

alias junk="xargs -I "{}" -- mv "{}" "~/.Trash" <<< "

Observe que isso tem efeitos colaterais em relação ao redirecionamento de fluxos.


0

Você não precisa fazer nada, o alias faz isso automaticamente.

Por exemplo, se eu quiser tornar o mestre de origem do git pull parametrizado, posso simplesmente criar um alias da seguinte maneira:

alias gpull = 'git pull origin '

e quando realmente chamá-lo, você pode passar 'master' (o nome do ramo) como um parâmetro, como este:

gpull master
//or any other branch
gpull mybranch

Isso não responde à pergunta, pois o argumento não pode ser colocado livremente, mas apenas no final.
dirdi
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.