Haga un alias de Bash que tome 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, supongo que esta ha sido implementada, pero me pregunto cómo.

Bash alias no acepta parámetros directamente. Deberá crear una función y alias eso.

alias no acepta parámetros, pero una función se puede llamar como 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 de Bash definidas en tu .bashrc y otros archivos están disponibles como comandos dentro de tu shell. Entonces, por ejemplo, puede llamar a la función anterior como esta

 $ myfunction original.conf my.conf 

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

 bash$ myfunction() { mv "$1" "$1.bak"; cp "$2" "$1"; } bash$ myfunction original.conf my.conf 

No olvides 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 "$@" ~/.Trash/; } 

O:

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

La pregunta simplemente se hace mal. No crea un alias que toma parámetros porque alias simplemente agrega un segundo nombre para algo que ya existe. La funcionalidad que OP quiere es el comando de function para crear una nueva función. No necesita alias la función ya que la función ya tiene un nombre.

Creo que quieres algo como esto:

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

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

TL; DR: Haz esto en cambio

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

 $ wrap_args() { echo "before $@ 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. Obtenga el resultado que desea, antes de que el lado oscuro controle para siempre su destino.

Aclaración

bash alias de bash aceptan argumentos, pero solo al final :

 $ 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 le gusta eludir las limitaciones y hacer lo que otros dicen que es imposible, esta es la receta. No me culpes si tu cabello se agota y tu cara queda cubierta de hollín al estilo científico.

La solución consiste en pasar los argumentos de que alias solo acepta al final una envoltura que los insertará en el medio y luego ejecutará su comando.

Solución 1

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

 $ alias wrap_args='f(){ echo before "$@" after; unset -ff; }; f' $ wrap_args xyz before xyz after 

Puede reemplazar $@ con $1 si solo quiere el primer argumento.

Explicación 1

Esto crea una función temporal f , que pasa los argumentos (tenga en cuenta que f se llama al final). El unset -f elimina la definición de la función ya que el alias se ejecuta para que no se cuelgue después.

Solución 2

También puedes usar una subshell:

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

Explicación 2

El alias crea un comando como:

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

Comentarios:

  • El marcador de posición _ es obligatorio, pero podría ser cualquier cosa. Se establece en sh $0 y es necesario para que el primer argumento dado por el usuario no se consum. Demostración:

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

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

    Aquí, los valores de los $0 y $@ del shell interactivo se reemplazan en el doble citado antes de pasar a sh . Aquí hay una prueba:

     echo "Consumed: $0 Printing: $@" 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 \$@ , pero la mejor práctica es citar sus argumentos (ya que pueden contener espacios), y \"\$@\" ve aún más feo, pero puede ayudarlo a ganar un concurso de ofuscación donde el cabello agotado es un requisito previo para la entrada.

Una solución alternativa es usar el marcador , una herramienta que he creado recientemente que le permite “marcar” plantillas de comando y colocar fácilmente el cursor en los titulares de lugares del comando:

marcador de línea de comando

Descubrí que la mayoría de las veces, estoy usando funciones de shell, así que no tengo que escribir comandos frecuentemente usados ​​una y otra vez en la línea de comandos. La cuestión del uso de 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. Marker objective es eliminar esa carga mental.

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'" 

.

 :<  

.

 :<  

.

 :<  /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

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 es la ‘redirección / fuente difícil de leer’. Además, hay fallas (que pensé que serían obvias, pero por si acaso estás confundido: ¡no me refiero a que realmente se usen … en ningún lado!)

………………………………………….. ………………………………………….. ……………………………………..

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 todas juntas. 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, dan o toman algunos personajes.

El verdadero kicker es la diferencia de tiempo, la parte superior es el "método de función" y el inferior es el método de "redirección de origen". Para probar esta teoría, el tiempo 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 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 es la parte inferior de unos 200 resultados, realizados a intervalos aleatorios. Parece que la creación / destrucción de funciones lleva más tiempo que la redirección. Espero que esto ayude a los futuros visitantes a esta pregunta (no quería quedárselo solo).

Para tomar parámetros, ¡debe usar funciones!

Sin embargo, $ @ se interpreta cuando se crea el alias en lugar de durante la ejecución del alias y tampoco escapa $. ¿Cómo resuelvo este problema?

Necesita usar la función de shell en lugar de un alias para deshacerse de este problema. Puedes definir foo de la siguiente manera:

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

O

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

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

 foo arg1 arg2 argN 

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

En tu caso, esto funcionará

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

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 los demás casos, se deben usar las 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. Entonces escribí algunas funciones para ese propósito. Sin embargo, hay ciertas variables que son (o pueden ser) modificadas por una llamada a la función en sí 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 defns de variable. en la forma 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 en un intérprete de comandos de shell interactivo (o no en ninguna función llama), FUNCNAME no está definido. Pero mi función me dice la información incorrecta:

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

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

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

Que da la salida correcta (ninguna) 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 números arbitrarios de argumentos.

Una solución general para pasar argumentos arbitrarios a un comando Bash Aliased:

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

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 

Una buena cosa acerca de esta solución es que todos los trucos especiales utilizados para manejar los parámetros posicionales (argumentos) a los comandos funcionarán al componer el comando atrapado. La única diferencia es que se debe usar la syntax de matriz.

P.ej,

Si quieres "$ @", usa "$ {CMD_ARGV [@]}".

Si quieres "$ #", usa "$ {# CMD_ARGV [@]}".

Etc.

Si está buscando una forma genérica para aplicar todos los parámetros a una función, no solo uno, dos o algún otro monto codificado, 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 "$@"; } alias runjar=runjar_fn; 

Entonces, en el ejemplo anterior, paso todos los parámetros desde cuando ejecuto runjar al alias.

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

Me gusta esta solución basada en $@ porque funciona con cualquier cantidad de params.

Las funciones son, de hecho, casi siempre la respuesta, como ya se ha contribuido y confirmado ampliamente en esta cita de la página del manual: “Para casi cualquier propósito, los alias son reemplazados por funciones de shell”.

Para completitud y porque esto puede ser útil (una syntax levemente más ligera) se puede observar que cuando los parámetros siguen el alias, todavía se pueden usar (aunque esto no cubriría los requisitos 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 esperaba, como: ssh -O stop myhost

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

Sin embargo, no necesita funciones al crear alias en el archivo .bashrc . Por ejemplo

 # create an alias that takes port-number by the user alias serve="python -m SimpleHTTPServer $1" 

Después de realizar el cambio en el archivo .bashrc, asegúrese de ingresar el siguiente comando.

 ~$ source .bashrc 

Deberías poder usar esto como tal

 ~$ serve 8998