web-dev-qa-db-esp.com

Hacer un alias Bash que toma un parámetro?

Solía ​​usar CShell ( csh ), que te permite crear un alias que toma un parámetro. La notación era algo así como

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

En Bash, esto no parece funcionar. Dado que Bash tiene una multitud de características útiles, asumo que esta ha sido implementada pero me pregunto cómo.

1040
Hello

Bash alias no acepta parámetros directamente. Tendrás que crear una función.

alias no acepta parámetros, pero se puede llamar a una función como si fuera un alias. Por ejemplo:

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


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

Por cierto, las funciones Bash definidas en su .bashrc y otros archivos están disponibles como comandos dentro de su Shell. Así, por ejemplo, puedes llamar a la función anterior como esta

$ myfunction original.conf my.conf
1766
arunkumar

Al refinar la respuesta anterior, puede obtener una sintaxis de 1 línea como puede para los alias, que es más conveniente para las definiciones ad-hoc en un Shell o en archivos .bashrc:

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

bash$ myfunction original.conf my.conf

No olvide el punto y coma antes del cierre del corchete derecho. Del mismo modo, para la pregunta real:

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

bash$ junk() { mv "[email protected]" ~/.Trash/; }

O:

bash$ junk() { for item in "[email protected]" ; do echo "Trashing: $item" ; mv "$item" ~/.Trash/; done; }
170
Mike Gleason

La pregunta simplemente se hace mal. No crea un alias que tome parámetros porque alias solo agrega un segundo nombre para algo que ya existe. La funcionalidad que desea el OP es el comando function para crear una nueva función. No es necesario asignar un alias a la función, ya que la función ya tiene un nombre.

Creo que quieres algo como esto:

function trash() { mv "[email protected]" ~/.Trash; }

¡Eso es! Puede usar los parámetros $ 1, $ 2, $ 3, etc., o simplemente rellenarlos con $ @

94
Evan Langlois

TL; DR: haz esto en su lugar

Es mucho más fácil y más legible usar una función que un alias para poner argumentos en medio de un comando.

$ wrap_args() { echo "before [email protected] after"; }
$ wrap_args 1 2 3
before 1 2 3 after

Si sigue leyendo, aprenderá cosas que no necesita saber sobre el procesamiento de argumentos de Shell. El conocimiento es peligroso. Simplemente obtenga el resultado que desea, antes de que el lado oscuro controle su destino para siempre.

Aclaración

bash aliases do aceptar argumentos, pero solo en end :

$ alias speak=echo
$ speak hello world
hello world

Poner argumentos en el medio del comando a través de alias es posible, pero se pone feo.

¡No intentes esto en casa, niños!

Si te gusta eludir las limitaciones y hacer lo que otros dicen que es imposible, aquí está la receta. Pero no me culpes si tu cabello se te raspa y tu cara termina cubierta de hollín al estilo científico loco.

La solución es pasar los argumentos que alias solo acepta al final a un contenedor que los insertará en el medio y luego ejecutará su comando.

Solución 1

Si realmente estás en contra de usar una función per se, puedes usar:

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

Puede reemplazar [email protected] con $1 si solo quiere el primer argumento.

Explicación 1

Esto crea una función temporal f, a la que se le pasan los argumentos (tenga en cuenta que f se llama al final). El unset -f elimina la definición de la función a medida que se ejecuta el alias para que no se quede atrás.

Solucion 2

También puedes usar una subshell:

$ alias wrap_args='sh -c '\''echo before "[email protected]" after'\'' _'

Explicación 2

El alias construye un comando como:

sh -c 'echo before "[email protected]" after' _

Comentarios:

  • El marcador de posición _ es obligatorio, pero podría ser cualquier cosa. Se establece en sh 's $0, y se requiere para que los primeros argumentos dados por el usuario no se consuman. Demostración:

    sh -c 'echo Consumed: "$0" Printing: "[email protected]"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
    
  • Se requieren las comillas simples dentro de comillas simples. Aquí hay un ejemplo de que no funciona con comillas dobles:

    $ sh -c "echo Consumed: $0 Printing: [email protected]" alcohol drunken babble
    Consumed: -bash Printing:
    

    En este caso, los valores de $0 y [email protected] de la Shell interactiva se reemplazan en la comilla doble antes de se pasa a sh. Aquí está la prueba:

    echo "Consumed: $0 Printing: [email protected]"
    Consumed: -bash Printing:
    

    Las comillas simples aseguran que estas variables no sean interpretadas por el Shell interactivo, y se pasan literalmente a sh -c.

    Podría usar comillas dobles y \[email protected], pero la mejor práctica es citar sus argumentos (ya que pueden contener espacios), y \"\[email protected]\" parece aún más feo, pero puede ayudarlo a ganar un concurso de ofuscación en el que el cabello desgarrado es un requisito previo para el ingreso.

82
Tom Hale

Una solución alternativa es usar marker , una herramienta que he creado recientemente que le permite "marcar" plantillas de comandos y colocar fácilmente el cursor en los marcadores de posición de comando:

commandline marker

Descubrí que la mayoría de las veces, uso funciones de Shell, por lo que no tengo que escribir los comandos de uso frecuente una y otra vez en la línea de comandos. El problema de usar funciones para este caso de uso es agregar nuevos términos a mi vocabulario de comando y tener que recordar a qué funciones se refieren los parámetros en el comando real. El objetivo del marcador es eliminar esa carga mental.

32
Amine Hajyoussef

Aquí hay tres ejemplos de funciones que tengo en mi ~/.bashrc, que son esencialmente alias que aceptan un parámetro:

#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
}

Referencias:

9
aliteralmind

NB: En caso de que la idea no sea obvia, es una mala idea usar alias para cualquier cosa que no sean alias, la primera es la "función en un alias" y la segunda la "redirección/fuente difícil de leer". Además, hay fallas (que yo pensamiento sería obvio, pero en caso de que estés confundido: no quiero que se utilicen realmente ... ¡en cualquier lugar!)

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

He respondido esto antes, y siempre ha sido así en el pasado:

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

lo cual es bueno y bueno, a menos que esté evitando el uso de funciones en conjunto. en cuyo caso puede aprovechar la gran capacidad de bash para redirigir el texto:

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

Ambos tienen aproximadamente la misma longitud de dar o tomar unos pocos caracteres.

El real kicker es la diferencia de tiempo, la parte superior es el "método de función" y la parte inferior el método de "fuente de redirección". Para probar esta teoría, el momento habla por sí mismo:

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
[email protected] /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
[email protected] /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
[email protected] /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
[email protected] /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 es la parte inferior de unos 200 resultados, realizados a intervalos aleatorios. Parece que la creación/destrucción de una función lleva más tiempo que la redirección. Esperemos que esto ayude a los futuros visitantes a esta pregunta (no quería guardármelo para mí).

6
osirisgothra

Si está buscando una forma genérica de aplicar todos los parámetros a una función, no solo uno o dos o alguna otra cantidad codificada, puede hacerlo de esta manera:

#!/usr/bin/env bash

# you would want to `source` this file, maybe in your .bash_profile?
function runjar_fn(){
    Java -jar myjar.jar "[email protected]";
}

alias runjar=runjar_fn;

Así que en el ejemplo anterior, paso todos los parámetros cuando ejecuto runjar al alias.

Por ejemplo, si hiciera runjar hi there, terminaría ejecutando Java -jar myjar.jar hi there. Si hiciera runjar one two three se ejecutaría Java -jar myjar.jar one two three.

Me gusta esta solución basada en [email protected] porque funciona con cualquier número de parámetros.

3
Micah

Hay razones técnicas legítimas para querer una solución generalizada al problema de bash alias que no tiene un mecanismo para tomar una posición de argumentos arbitrarios. Una razón es si el comando que desea ejecutar se vería afectado negativamente por los cambios en el entorno que resultan de la ejecución de una función. En todos otros casos, se deben utilizar funciones.

Lo que recientemente me obligó a intentar una solución a esto es que quería crear algunos comandos abreviados para imprimir las definiciones de variables y funciones. Así que escribí algunas funciones para ese propósito. Sin embargo, hay ciertas variables que son (o pueden ser) cambiadas por una función llamada misma. Entre ellos están:

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

El comando básico que había estado usando (en una función) para imprimir las definiciones de variables. en el formulario de salida por el comando set fue:

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

P.ej.:

> V=voodoo
sv V
V=voodoo

Problema: esto no imprimirá las definiciones de las variables mencionadas anteriormente, ya que están en el contexto actual , por ejemplo, si está en una solicitud de shell interactiva (o no en las llamadas a funciones), FUNCNAME no está definido . Pero mi función me dice la información incorrecta:

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

Una solución que encontré ha sido mencionada por otros en otras publicaciones sobre este tema. Para este comando específico para imprimir las definiciones de variable, y que requiere solo un argumento, hice esto:

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

Lo que da el resultado correcto (ninguno) y el estado del resultado (falso):

> asv FUNCNAME
> echo $?
1

Sin embargo, todavía me sentí obligado a encontrar una solución que funcione para un número arbitrario de argumentos.

Una solución general para pasar argumentos arbitrarios a un comando con alias de 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([email protected])"; 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=("[email protected]");  # 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".

P.ej.:

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

Algo bueno de esta solución es que todos los trucos especiales utilizados para manejar los parámetros posicionales (argumentos) a los comandos funcionarán cuando se componga el comando atrapado. La única diferencia es que debe utilizarse la sintaxis de matriz.

P.ej.,

Si desea "$ @", use "$ {CMD_ARGV [@]}".

Si desea "$ #", use "$ {# CMD_ARGV [@]}".

Etc.

2
crobc1

Para tomar parámetros, debes usar funciones!

Sin embargo, $ @ se interpreta al crear el alias en lugar de hacerlo durante la ejecución del alias y al escapar de $ tampoco funciona. ¿Cómo resuelvo este problema?

Necesitas usar la función Shell en lugar de un alias para deshacerte de este problema. Puedes definir foo de la siguiente manera:

function foo() { /path/to/command "[email protected]" ;}

OR

foo() { /path/to/command "[email protected]" ;}

Finalmente, llame a su foo () usando la siguiente sintaxis:

foo arg1 arg2 argN

Asegúrese de agregar su archivo foo () a ~/.bash_profile o ~/.zshrc.

En tu caso, esto funcionará.

function trash() { mv [email protected] ~/.Trash; }
2
Ahmad Awais

Todo lo que tienes que hacer es hacer una función dentro de un alias:

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

Usted debe poner comillas dobles alrededor de "$ 1" porque las comillas simples no funcionarán.

2
Ken Tran

Aunque una función es generalmente una buena opción como otras han dicho aquí. Pero me gustaría señalar que, hay casos en que una función no funciona y un alias, u otros casos, un script de envoltura es una mejor elección.

el alias funciona, la función no, y uno todavía puede pasar parámetros.

Por ejemplo: quiero hacer un acceso directo para activar conda y crear un entorno conda en un comando. Estaría tentado de hacer esto:

function conda_activate(){
    export PATH="$PATH:/path/to/conda"
    envname=$1
    if [ "x$envname" -ne "x" ]; then
       source activate "$envname"
    fi
}

Esto no funciona según lo previsto. Si uno ejecuta conda_activate myenv. El comando source realiza el suministro pero se cierra de inmediato, y el Shell en ejecución no tiene el entorno.

Una solución de trabajo se ve así:

function conda_activate(){
    export PATH="$PATH:/path/to/conda"
    # do some other things
}
alias conda_env='conda_activate; source activate'
# usage example
conda_env myenv

El script wrapper es una mejor opción

Me ha ocurrido varias veces que no se puede encontrar un alias o una función cuando se registra a través de ssh o que involucra el cambio de nombres de usuario o un entorno multiusuario. Hay consejos y trucos con los archivos de puntos de origen, por ejemplo, este interesante: alias sd='Sudo ' permite que alias install='sd apt-get install' funcione como se espera. Observe el espacio adicional en sd='Sudo '. Pero cuando tenga dudas, un script de envoltura es siempre la solución más confiable y portátil.

1
biocyberman

De hecho, las funciones son casi siempre la respuesta, como ya se ha contribuido y confirmado ampliamente por esta cita de la página del manual: "Para casi todos los propósitos, los alias son reemplazados por las funciones de Shell".

Por completo y porque esto puede ser útil (una sintaxis ligeramente más liviana), se puede observar que cuando los parámetros siguen el alias, aún pueden usarse (aunque esto no abordaría el requisito del OP). Esto es probablemente más fácil de demostrar con un ejemplo:

alias ssh_disc='ssh -O stop'

me permite escribir smth como ssh_disc myhost, que se expande como se espera como: ssh -O stop myhost

Esto puede ser útil para comandos que toman argumentos complejos (mi memoria ya no es lo que usa t be ...)

0
sxc731