web-dev-qa-db-fra.com

Faire un alias Bash qui prend un paramètre?

J'avais l'habitude d'utiliser CShell ( csh ), ce qui vous permet de créer un alias prenant un paramètre. La notation était quelque chose comme

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

Dans Bash, cela ne semble pas fonctionner. Etant donné que Bash a une multitude de fonctionnalités utiles, je suppose que celui-ci a été implémenté, mais je me demande comment.

1107
Hello

Alias ​​Bash n'accepte pas directement les paramètres. Vous devrez créer une fonction.

alias n'accepte pas les paramètres, mais une fonction peut être appelée comme un alias. Par exemple:

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


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

Soit dit en passant, les fonctions Bash définies dans votre .bashrc et d'autres fichiers sont disponibles sous forme de commandes dans votre shell. Donc, par exemple, vous pouvez appeler la fonction précédente comme celle-ci

$ myfunction original.conf my.conf
1865
arunkumar

En affinant la réponse ci-dessus, vous pouvez obtenir une syntaxe sur une ligne, comme pour les alias, ce qui est plus pratique pour les définitions ad-hoc dans un fichier Shell ou .bashrc:

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

bash$ myfunction original.conf my.conf

N'oubliez pas le point-virgule avant le dernier crochet de droite. De même, pour la question actuelle:

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

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

Ou:

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

La question est simplement posée mal. Vous ne créez pas d'alias prenant des paramètres car alias ajoute simplement un deuxième nom pour quelque chose qui existe déjà. La fonctionnalité souhaitée par l'OP est la commande function pour créer une nouvelle fonction. Vous n'avez pas besoin d'aliaser la fonction car celle-ci a déjà un nom.

Je pense que tu veux quelque chose comme ça:

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

C'est ça! Vous pouvez utiliser les paramètres $ 1, $ 2, $ 3, etc., ou simplement les farcir tous avec $ @

103
Evan Langlois

TL; DR: Faites ceci à la place

Il est beaucoup plus facile et plus lisible d'utiliser une fonction qu'un alias pour placer des arguments au milieu d'une commande.

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

Si vous continuez votre lecture, vous apprendrez des choses que vous n'avez pas besoin de savoir sur le traitement des arguments de Shell. La connaissance est dangereuse. Obtenez le résultat souhaité avant que le côté obscur ne contrôle votre destin pour toujours.

Clarification

bash les alias acceptent les arguments , mais uniquement à l'extrémité :

$ alias speak=echo
$ speak hello world
hello world

Mettre des arguments dans le milieu de la commande via alias est certes possible, mais ça devient moche.

N'essayez pas ça à la maison, les enfants!

Si vous aimez contourner les limitations et faire ce que les autres disent est impossible, voici la recette. Juste ne me blâmez pas si vos cheveux se frôlent et que votre visage finit par se couvrir de suie à la manière d'un savant fou.

La solution consiste à transmettre les arguments que alias accepte uniquement à la fin à un wrapper qui les insérera au milieu, puis exécutera votre commande.

Solution 1

Si vous êtes vraiment contre l'utilisation d'une fonction en tant que telle, vous pouvez utiliser:

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

Vous pouvez remplacer $@ par $1 si vous ne voulez que le premier argument.

explication 1

Cela crée une fonction temporaire f, à laquelle sont transmis les arguments (notez que f est appelé à la toute fin). Le unset -f supprime la définition de la fonction car l'alias est exécuté afin qu'il ne reste pas après.

Solution 2

Vous pouvez également utiliser un sous-shell:

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

explication 2

L'alias construit une commande comme:

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

Commentaires:

  • L'espace réservé _ est requis, mais il peut s'agir de n'importe quoi. Il est défini sur le $0 de sh et est requis pour que le premier des arguments fournis par l'utilisateur ne soit pas consommé. Manifestation:

    sh -c 'echo Consumed: "$0" Printing: "$@"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
    
  • Les guillemets simples à l'intérieur des guillemets simples sont obligatoires. Voici un exemple qui ne fonctionne pas avec les guillemets:

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

    Ici, les valeurs de $0 et $@ du shell interactif sont remplacées par les guillemets doubles avant avant d’être passés à sh. Voici la preuve:

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

    Les guillemets simples garantissent que ces variables ne sont pas interprétées par le shell interactif et sont transmises littéralement à sh -c.

    Vous pouvez utiliser des guillemets doubles et \$@, mais la meilleure pratique consiste à citer vos arguments (car ils peuvent contenir des espaces). \"\$@\" est encore plus laid, mais peut vous aider à remporter un concours d’obscurcissement dans lequel les cheveux écorchés sont une condition préalable à l’entrée.

94
Tom Hale

Une autre solution consiste à utiliser marqueur , un outil que j'ai créé récemment et qui vous permet de "marquer" les modèles de commande et de placer facilement le curseur sur les contrôleurs de la commande:

commandline marker

J'ai constaté que la plupart du temps, j'utilise des fonctions de shell, ce qui évite d'avoir à écrire des commandes fréquemment utilisées encore et encore dans la ligne de commande. Le problème de l’utilisation de fonctions pour ce cas d’utilisation consiste à ajouter de nouveaux termes à mon vocabulaire de commande et à devoir me rappeler les paramètres des fonctions dans la commande real. L'objectif du marqueur est d'éliminer ce fardeau mental.

35
Amine Hajyoussef

Voici trois exemples de fonctions présentes dans mon ~/.bashrc, qui sont essentiellement des alias acceptant un paramètre:

#Utility required by all below functions.
#https://stackoverflow.com/questions/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
}

Références:

10
aliteralmind

Tout ce que vous avez à faire est de créer une fonction dans un alias:

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

Vous devez mettre des guillemets doubles autour de "$ 1" car les guillemets simples ne fonctionneront pas.

6
Ken Tran

Si vous recherchez un moyen générique d'appliquer tous les paramètres à une fonction, pas seulement un ou deux ou un autre montant codé en dur, vous pouvez le faire comme suit:

#!/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;

Donc, dans l'exemple ci-dessus, je passe tous les paramètres de quand je lance runjar à l'alias.

Par exemple, si je faisais runjar hi there, cela finirait par exécuter Java -jar myjar.jar hi there. Si je faisais runjar one two three il exécuterait Java -jar myjar.jar one two three.

J'aime cette solution basée sur $@ car elle fonctionne avec un nombre quelconque de paramètres.

5
Micah

NB: Si l'idée n'est pas évidente, il est déconseillé d'utiliser des alias pour des noms autres que des alias, le premier étant la "fonction dans un alias" et le second étant le "disque difficile à lire redirect/source". De plus, il y a des défauts (que je pensée serait évident, mais juste au cas où vous seriez confus: je ne veux pas les utiliser réellement ... n'importe où!)

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

J'ai déjà répondu à cela, et ça a toujours été comme ça dans le passé:

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

ce qui est bien et bon, à moins que vous évitiez l'utilisation de toutes les fonctions. Dans ce cas, vous pouvez tirer parti de la grande capacité de bash de rediriger du texte:

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

Ils ont tous les deux à peu près la même longueur, à peu près quelques caractères.

Le kicker réel est la différence de temps, le haut étant la "méthode de la fonction" et le bas étant la méthode "de la source de redirection". Pour prouver cette théorie, le timing parle de lui-même:

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

Ceci est la partie inférieure d'environ 200 résultats, effectués à des intervalles aléatoires. Il semble que la création/destruction de fonctions prenne plus de temps que la redirection. J'espère que cela aidera les futurs visiteurs à cette question (je ne voulais pas le garder pour moi).

5
osirisgothra

Il existe des raisons techniques légitimes de vouloir une solution généralisée au problème de bash alias ne disposant pas de mécanisme permettant de repositionner des arguments arbitraires. Une des raisons est que si les modifications apportées à l'environnement à la suite de l'exécution d'une fonction affectent négativement la commande que vous souhaitez exécuter. Dans tous autres cas, les fonctions doivent être utilisées.

Récemment, ce qui m’a poussé à essayer de résoudre ce problème, c’est que je voulais créer des commandes abrégées pour imprimer les définitions de variables et de fonctions. J'ai donc écrit quelques fonctions à cette fin. Cependant, certaines variables sont (ou peuvent être) modifiées par un appel de fonction lui-même. Parmi eux:

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

La commande de base que j'avais utilisée (dans une fonction) pour imprimer des définitions de variables. dans le formulaire affiché par la commande set était:

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

Par exemple.:

> V=voodoo
sv V
V=voodoo

Problème: Cela n’imprimera pas les définitions des variables mentionnées ci-dessus car elles se trouvent dans le contexte actuel , par exemple, dans une invite de shell interactive (ou dans aucun appel de fonction), FUNCNAME n’est pas défini. Mais ma fonction me dit la mauvaise information:

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

Une solution que j'ai proposée a été mentionnée par d'autres dans d'autres publications sur ce sujet. Pour que cette commande spécifique imprime une valeur par défaut, et qui ne nécessite qu'un seul argument, j'ai fait ceci:

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

Ce qui donne la sortie correcte (aucune) et l’état du résultat (faux):

> asv FUNCNAME
> echo $?
1

Cependant, je me sentais toujours obligé de trouver une solution qui fonctionne pour un nombre arbitraire d'arguments.

ne solution générale pour passer des arguments arbitraires à une commande Bash Alias:

# (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".

Par exemple.:

> . 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

Une bonne chose à propos de cette solution est que toutes les astuces spéciales utilisées pour gérer les paramètres de position (arguments) des commandes fonctionneront lors de la composition de la commande piégée. La seule différence est que la syntaxe de tableau doit être utilisée.

Par exemple.,

Si vous voulez "$ @", utilisez "$ {CMD_ARGV [@]}".

Si vous voulez "$ #", utilisez "$ {# CMD_ARGV [@]}".

Etc.

3
crobc1

Pour prendre des paramètres, vous devez utiliser des fonctions!

Cependant, $ @ get interprété lors de la création de l’alias au lieu de lors de l’exécution de celui-ci et lors de l’échappement de la variable $ ne fonctionne pas non plus. Comment résoudre ce problème?

Vous devez utiliser la fonction shell au lieu d'un alias pour résoudre ce problème. Vous pouvez définir foo comme suit:

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

OR

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

Enfin, appelez votre foo () en utilisant la syntaxe suivante:

foo arg1 arg2 argN

Assurez-vous d’ajouter votre foo () au fichier ~/.bash_profile ou ~/.zshrc.

Dans votre cas, cela fonctionnera

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

Les fonctions sont en effet presque toujours la réponse, comme l'a déjà amplement contribué et confirmé par cette citation de la page de manuel: "Dans presque tous les cas, les alias sont remplacés par des fonctions Shell."

Pour être complet et parce que cela peut être utile (syntaxe légèrement plus légère), on peut noter que lorsque le ou les paramètres suivent l'alias, ils peuvent toujours être utilisés (bien que cela ne réponde pas à l'exigence du PO). C'est probablement le plus facile à démontrer avec un exemple:

alias ssh_disc='ssh -O stop'

me permet de taper smth comme ssh_disc myhost, qui s’agrandit comme prévu comme suit: ssh -O stop myhost

Cela peut être utile pour les commandes qui prennent des arguments complexes (ma mémoire n’est plus ce qu’elle utilise ...)

1
sxc731

Les fonctions et les alias peuvent utiliser des paramètres comme d'autres l'ont montré ici. De plus, j'aimerais souligner quelques autres aspects:

1. fonction s'exécute dans sa propre portée, alias partage la portée

Il peut être utile de connaître cette différence dans les cas où vous devez cacher ou exposer quelque chose. Cela suggère également qu'une fonction est le meilleur choix pour l'encapsulation.

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 

Sortie:

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. le script wrapper est un meilleur choix

Il m'est arrivé à plusieurs reprises de ne pas trouver un alias ou une fonction lorsque se connectant via ssh ou impliquant un changement de nom d'utilisateur ou un environnement multi-utilisateur. Il existe des trucs et astuces pour trouver des fichiers de points ou cet intéressant avec un alias: alias sd='Sudo ' laisse cet alias suivant alias install='sd apt-get install' fonctionner comme prévu (notez l'espace supplémentaire dans sd='Sudo '). Cependant, un script wrapper fonctionne mieux qu'une fonction ou un alias dans des cas comme celui-ci. L’avantage principal d’un script de wrapper est qu’il est visible/exécutable dans le chemin sous-prévu (c’est-à-dire/usr/loca/bin /) où une fonction/alias doit être recherché avant de pouvoir être utilisé. Par exemple, vous insérez une fonction dans un fichier ~/.bash_profile ou ~/.bashrc pour bash, mais vous basculez plus tard vers un autre shell (c'est-à-dire zsh). La fonction n'est alors plus visible. Ainsi, en cas de doute, un script wrapper est toujours la solution la plus fiable et la plus portable.

1
biocyberman