passing - bashrc alias with arguments
Hacer un alias Bash que toma un parámetro? (11)
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.
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 $@ 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
alias de bash
aceptan argumentos, pero solo al final :
$ alias speak=echo
$ speak hello world
hello world
Poner argumentos en medio del comando a través de un 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 el alias
acepta solo 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 "$@" after; unset -f f; }; f''
$ wrap_args x y z
before x y z after
Puede reemplazar $@
con $1
si solo desea el primer argumento.
Explicación 1
Esto crea una función temporal f
, a la que se pasan los argumentos (tenga en cuenta que f
se llama al final). 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 "$@" after''/''' _''
Explicación 2
El alias construye un comando como:
sh -c ''echo before "$@" after'' _
Comentarios:
El marcador de posición
_
es obligatorio, pero podría ser cualquier cosa. Se establece ensh
$0
s$0
, y se requiere para que no se consuman los primeros argumentos dados por el usuario. Demostración:sh -c ''echo Consumed: "$0" Printing: "$@"'' 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: $@" alcohol drunken babble Consumed: -bash Printing:
Aquí, los valores de
$0
y$@
del shell interactivo se reemplazan en la cita doble antes de que se pase ash
. Aquí está la 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 en el que el cabello al descubierto Es un requisito previo para la entrada.
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 "$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 "$@" ~/.Trash/; }
O:
bash$ junk() { for item in "$@" ; do echo "Trashing: $item" ; mv "$item" ~/.Trash/; done; }
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://.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://.com/a/3232082/2736496
- Alias w/param: https://.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://.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:
- Recorte de espacios en blanco a partir de cadenas: ¿Cómo recortar espacios en blanco de una variable Bash?
- Saltos de línea reales: https://.com/a/4296147/2736496
- Alias w / param: https://.com/a/7131683/2736496 (otra respuesta en esta pregunta)
- HISTIGNORE: https://superuser.com/questions/232885/can-you-share-wisdom-on-using-histignore-in-bash
- Indicador S / N: https://.com/a/3232082/2736496
- Elimine todos los elementos coincidentes del historial: https://unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string
- Es una cadena nula / vacía: http://tldp.org/LDP/abs/html/comparison-ops.html
Bash alias no acepta parámetros directamente. Tendrás que crear una función y alias que.
alias
no acepta parámetros, pero una función puede llamarse 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 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
De hecho, las funciones son casi siempre la respuesta, como ya se ha contribuido y confirmado ampliamente con esta cita de la página del manual: "Para casi todos los propósitos, los alias son reemplazados por las funciones de shell".
Para completar y porque esto puede ser útil (una sintaxis ligeramente más liviana) se podría notar 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 algo 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 ...)
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 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 en un indicador de shell interactivo (o no en cualquier llamada a función), 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($@)"; 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
Lo 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 se debe usar la sintaxis de matriz.
P.ej,
Si desea "$ @", use "$ {CMD_ARGV [@]}".
Si desea "$ #", use "$ {# CMD_ARGV [@]}".
Etc.
La pregunta simplemente se hace mal. No creas un alias que tome parámetros porque el alias
solo agrega un segundo nombre para algo que ya existe. La funcionalidad que desea el OP es el comando de 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 "$@" ~/.Trash; }
¡Eso es! Puede usar los parámetros $ 1, $ 2, $ 3, etc., o simplemente rellenarlos con $ @
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 pensé que serían obvias, pero en caso de que estés confundido: no quiero que se usen realmente ... ¡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 todas las funciones. en cuyo caso puede aprovechar la gran capacidad de bash para redirigir 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 kicker real 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
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. Esperemos que esto ayude a los futuros visitantes a esta pregunta (no quería guardármelo para mí).
Para tomar parámetros, debes usar funciones!
Sin embargo, $ @ se interpreta cuando se crea el alias en lugar de durante la ejecución del alias y el escape de $ tampoco funciona. ¿Cómo resuelvo este problema?
Es necesario utilizar 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 sintaxis:
foo arg1 arg2 argN
Asegúrese de agregar su foo () al archivo ~/.bash_profile
o ~/.zshrc
.
En tu caso, esto funcionará.
function trash() { mv $@ ~/.Trash; }
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 "$@";
}
alias runjar=runjar_fn;
Así que en el ejemplo anterior, paso todos los parámetros de cuando ejecuto runjar
al alias.
Por ejemplo, si runjar hi there
, terminaría ejecutando java -jar myjar.jar hi there
. Si runjar one two three
, ejecutaría java -jar myjar.jar one two three
.
Me gusta esta solución basada en $@
porque funciona con cualquier número de parámetros.
Una solución alternativa es usar el marker , una herramienta que he creado recientemente que le permite "marcar" plantillas de comando y colocar fácilmente el cursor en los marcadores de posición del comando:
Descubrí que la mayoría de las veces, utilizo 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.