¿Hacer un alias Bash que tome un parámetro?


1273

Solía ​​usar CShell (), que le 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 se ha implementado, pero me pregunto cómo.



2
Asegúrese de usar citas alrededor de los argumentos"$1"
Christophe Roussy

Esta pregunta está fuera de tema para SO. Fue contestada en UNIX.SE , y la respuesta es que usted ni siquiera tiene que molestarse: "Por ejemplo, si usted fuera a alias lsa ls -la, a continuación, escribiendo ls foo barsería realmente ejecutar ls -la foo baren la línea de comandos."
Dan Dascalescu

77
eso no ayudaría a interpolar una variable en el medio de una cadena
Franz Sittampalam

1
Aquí está el alias de prueba lil que usé para descubrir esta falacia ... alias test_args="echo PREFIX --$1-- SUFFIX", que cuando se llama con test_args ABCDproduce la siguiente salida de la consolaPREFIX ---- SUFFIX ABCD
jxramos

Respuestas:


2126

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

aliasno 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 usted .bashrcy otros archivos están disponibles como comandos dentro de su shell. Entonces, por ejemplo, puede llamar a la función anterior de esta manera

$ myfunction original.conf my.conf

48
¿Debería envolverme $1entre comillas?
Jürgen Paul

263
Ni siquiera tiene que declarar el alias. Simplemente definir la función servirá.
dinigo

207
Si está cambiando un alias a una función, sourceing .bashrc agregará la función, pero no eliminará el alias anterior. Como los alias tienen un precedente más alto que las funciones, intentará usar el alias. Debe cerrar y volver a abrir su shell, o bien llamar unalias <name>. Quizás ahorre a alguien los 5 minutos que acabo de perder.
Marty Neal

56
@MartinNeal: Un truco para ahorrar tiempo que aprendí en Sun es simplemente hacer un exec bash: Comenzará un nuevo shell, que le dará una lectura limpia de sus configuraciones, como si cerrara y volviera a abrir, pero manteniendo la configuración variable de entorno de esa sesión también . Además, la ejecución bashsin el exec puede ser útil cuando desea manejar piensa como una pila.
Macneil Shonle

21
@mich: sí, siempre ponga las variables bash entre comillas. por ej mv "$1" "$1.bak". sin comillas, si $ 1 fuera "hola mundo", ejecutarías en mv hello world hello world.baklugar de mv "hello world" "hello world.bak".
orion elenzil

208

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

O:

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

3
Prefiero esta respuesta, ya que muestra la iteración a través de la matriz de argumentos que pueden aparecer. $ 1 y $ 2 son solo casos especiales, lo que también se ilustra en esta respuesta.
philo vivero

16
Cambie mv "$1" "$1.bak"; cp "$2" "$1" a mv "$1" "$1.bak" && cp "$2" "$1"para no perder sus datos cuando tenga mvproblemas (por ejemplo, sistema de archivos lleno).
Henk Langeveld

3
"No olvides el punto y coma antes del cierre del corchete derecho". Muchas veces esto! Gracias :)
Kaushal Modi

Y también se requieren los espacios después del primer paréntesis y antes del último paréntesis.
Bryan Chapel

1
@HenkLangeveld Si bien su comentario es perfectamente correcto, muestra que ha estado presente por un tiempo; no sabría cuándo me encontré por última vez con el error "no queda espacio en el dispositivo" ;-). (¡Sin embargo, lo uso regularmente cuando ya no encuentro espacio en el mostrador de la cocina!) Parece que no sucede muy a menudo en estos días.
Peter - Restablece a Mónica el

124

La pregunta simplemente se hace mal. No crea un alias que tome parámetros porque aliassolo agrega un segundo nombre para algo que ya existe. La funcionalidad que el OP quiere es el functioncomando para crear una nueva función. No necesita alias de 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 $ @


77
Esta respuesta lo dice todo. Si hubiera leído desde el final de esta página, habría ahorrado algo de tiempo.
Joe

-1 Un alias y una función no son 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
Fuzzy Logic

Realmente debería citar $@para admitir nombres de archivos con espacios, etc.
Tom Hale

Se suponía que era una explicación general de que no tiene parámetros de alias, sino que utiliza una función. La función dada fue solo un ejemplo para contrarrestar el ejemplo original. No creo que la persona estuviera buscando una función de basura de uso general. Sin embargo, en aras de presentar un mejor código, he actualizado la respuesta.
Evan Langlois

1
@FuzzyLogic: pasé unos minutos intentando descomprimir el código en tu comentario. Es un gran esfuerzo (interesante) realizar un paquete de código en un comentario donde no se puede formatear correctamente. Todavía no he descubierto exactamente qué hace o, lo que es más importante, por qué respalda su afirmación (correcta).
Joe

110

TL; DR: haga esto en su lugar

Es mucho más fácil y más legible usar una función que un alias para colocar 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. Solo obtén el resultado que deseas, antes de que el lado oscuro controle para siempre tu destino.

Aclaración

bashalias hacen aceptar los argumentos, pero sólo al final :

$ alias speak=echo
$ speak hello world
hello world

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

¡No intenten esto en casa, niños!

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

La solución consiste en pasar los argumentos que aliasaceptan solo al final a un contenedor 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 -f f; }; f'
$ wrap_args x y z
before x y z after

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

Explicacion 1

Esto crea una función temporal f, a la que se le pasan los argumentos (tenga en cuenta que fse llama al final). La unset -fquita la definición de función como el alias se ejecuta de manera que no cuelgue alrededor después.

Solución 2

También puedes usar una subshell:

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

Explicacion 2

El alias construye un comando como:

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

Comentarios:

  • Se _requiere el marcador de posición , pero podría ser cualquier cosa. Se establece en sh's $0, y es necesario para que el primero de los argumentos proporcionados por el usuario no se consuma. Demostración:

    sh -c 'echo Consumed: "$0" Printing: "$@"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
  • Se requieren 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 los shell interactivos $0y $@se reemplazan en el doble entrecomillado antes de pasarlo a sh. Aquí hay prueba:

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

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

    Puede usar comillas dobles y \$@, pero la mejor práctica es citar sus argumentos (ya que pueden contener espacios), y se \"\$@\"ve aún más feo, pero puede ayudarlo a ganar un concurso de ofuscación donde el cabello agitado es un requisito previo para la entrada.


2
para la solución 1, asegúrese de utilizar comillas simples y evite \ "en torno a $ @. Una técnica muy útil si la necesita. ty.
sgtz

La solución 1 funcionó para mí wrap rdesktop-vrdp con argumento para cada servidor que quiero.
Hsehdar

Aceptar argumentos al final es exactamente lo que necesitaba para reemplazar "git checkout {branchname}" con un simple "gc {branchname}". En .bash_profile simplemente tuve que agregaralias gc='git checkout'
AbstractVoid el

@sgtz ¿por qué quieres eliminar las comillas $@? Son necesarios si tiene, por ejemplo, archivos con espacios en ellos.
Tom Hale

@TomHale Esto es algo lingüístico, pero la solución 1 no es realmente útil si alguien realmente está en contra de usar una función (cualquiera que sea la razón) ya que usted directamente después de admitir que solo está cambiando la definición de una función al tiempo de ejecución de El alias. en segundo lugar, "los alias bash aceptan argumentos, pero solo al final" es más semántica: los alias son, por definición, sustituciones de palabras por cadenas. no necesitan aceptar argumentos, los comandos que se ejecutan a través de la sustitución, sin embargo, sí pueden. alias es un reemplazo de personaje, nada más y nada menos. ¿No?
BUFU

39

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 marcadores de posición de 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 usados ​​con frecuencia una y otra vez en la línea de comandos. El problema del uso de funciones para este caso de uso es agregar nuevos términos a mi vocabulario de comandos y tener que recordar a qué parámetros de funciones se refieren en el comando real. El objetivo del marcador es eliminar esa carga mental.


14

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

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

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


55
Eso es inteligente, pero aparte del hecho de que la pregunta especificó hacer un alias, ¿hay alguna razón para no solo hacer la función con el mismo nombre que el alias inicialmente?
xaxxon

1
@xaxxon En realidad no, pero es la forma más fácil que sé usar un alias, no una función.
Ken Tran

1
Esto no funciona. En Ubuntu 18, recibo un error bash: syntax error near unexpected token '{mkdir'.
Shital Shah

falta un final ;dentro de la función, _mkcdprobablemente el error es de eso.
Jetchisel

Deje espacio antes y después del {y}
hesham_EE

10

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

Referencias


¿Eh? Esa es una función, no un alias. Y una de sus referencias es esta misma pregunta.
tripleee

2
No, funciona exactamente como una función. Como explican varias de las respuestas aquí, no puede crear un alias que tome un parámetro. Lo que puedes hacer es escribir una función, que es lo que has hecho.
tripleee

1
@tripleee Desde mi punto de vista de bash newbie, antes de leer tu comentario, pensé que era un alias (una forma alternativa de crear uno). Funciona exactamente como uno, por lo que puedo decir. Es esencialmente un alias que acepta un parámetro, incluso si no estoy en la terminología. No veo el problema. He aclarado el enlace a la otra respuesta en este problema. Me ayudó a crear esto.
aliteralmind

1
Quizás esto no responde específicamente a la pregunta de "un alias bash que acepta un parámetro", porque ahora entiendo que no es posible, pero ciertamente lo responde efectivamente . ¿Que me estoy perdiendo aqui?
aliteralmind

2
Algunos ejemplos realmente buenos aquí, y un código muy bien comentado. Una gran ayuda para las personas que visitan esta página.
chim

9

Bash alias absolutamente acepta parámetros. Acabo de agregar un alias para crear una nueva aplicación de reacción que acepta el nombre de la aplicación como parámetro. Aquí está mi proceso:

Abra el bash_profile para editar en nano

nano /.bash_profile

Agregue sus alias, uno por línea:

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

nota: el "$ @" acepta parámetros pasados ​​como "creact my-new-app"

Guardar y salir del editor nano

ctrl + o para escribir (presione enter); Ctrl + X para salir

Dígale a la terminal que use los nuevos alias en .bash_profile

source /.bash_profile

¡Eso es! Ahora puedes usar tus nuevos alias


6

NB: en caso de que la idea no sea obvia, es una mala idea usar alias para cualquier cosa que no sea alias, siendo la primera 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 decir que realmente se usen ... ¡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 está bien y es bueno, a menos que esté evitando el uso de funciones todas juntas. 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, dan o toman algunos caracteres.

La verdadera patada es la diferencia horaria, la parte superior es el 'método de función' y la inferior es el método 'fuente de redireccionamiento'. 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, realizada 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 que me la guardara).


2
Tener un alias que defina una función, luego ejecutar esa función es una tontería. Solo escribe una función. Para casi todos los propósitos, los alias son reemplazados por las funciones de shell.
geirha

1
El segundo (el alias de barra) tiene varios efectos secundarios. En primer lugar, el comando no puede usar stdin. En segundo lugar, si no se proporcionan argumentos después del alias, se pasan todos los argumentos que tiene el shell. El uso de una función evita todos estos efectos secundarios. (También uso inútil del gato).
geirha

2
oye, nunca dije que era una buena idea hacer nada de esto, solo dije que también había otras formas de hacerlo: la idea principal aquí es que no debes usar funciones en alias porque son más lentas incluso que complicadas redirigir, pensé que habría sido obvio, pero supongo que tengo que explicarlo para todos (a lo que también me gritan porque la publicación está demasiado hinchada si lo hice en primer lugar)
osirisgothra

1
Dado que los tiempos de la barra son todos ceros, sospecharía que el shell no está sincronizando esto correctamente. ¿Observe que el mensaje de tiempo se imprime antes de ejecutar el comando? Ergo, no mide nada y luego ejecuta la barra, por lo que no estás midiendo la barra en absoluto. Haga algo más complejo o haga un gran bucle en el bar para que sea más obvio que no está cronometrando nada
Evan Langlois

1
Ahora intenté esto (aunque tuve que reemplazar el último __foo () por __foo). time for i in {1..1000}; do foo FOOVALUE; done→ 0m0.028s. Pero time for i in {1..1000}; do bar FOOVALUE; done→ 0m2.739s. Bar es dos órdenes de magnitud más lento que foo. El simple uso de una función simple en lugar de un alias reduce el tiempo de ejecución en otro 30%: function boo() { echo "arg1 for boo=$1" ; }time for i in {1..1000}; do boo FOOVALUE; done→ 0m0.019s.
Arne Babenhauserheide

6

Si está buscando una forma genérica de aplicar todos los parámetros a una función, no solo una o dos u 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;

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

Por ejemplo, si lo hiciera runjar hi there, terminaría realmente ejecutándose java -jar myjar.jar hi there. Si lo hiciera runjar one two three, correría java -jar myjar.jar one two three.

Me gusta esta $@solución basada porque funciona con cualquier número de parámetros.


44
¿Por qué no simplemente nombrar la función en runjarlugar de convertirla en un alias de una función? Parece innecesariamente complicado!
Evan Langlois

Las funciones de @EvanLanglois se editan automáticamente alias(o se pasan las propiedades de las cosas alias) en los archivos bash? en caso afirmativo, es solo que no lo he sabido
Micah

No entiendo lo que quieres decir. Un alias es otro nombre, ya sea una persona o una función. Entonces, hiciste una función, luego hiciste un segundo nombre para la función. El alias no tiene nada de especial, es solo un segundo nombre y no es obligatorio. Lo que escribe en la línea de comandos puede ser funciones internas o externas, por lo que "función" crea un nuevo comando, no un alias.
Evan Langlois

44
Esto es inutil. Es lo mismo que alias runjar='java -jar myjar.jar'. Los alias aceptan argumentos, pero solo al final.
Tom Hale

6

Respetuosamente a todos aquellos que dicen que no se puede insertar un parámetro en el medio de un alias. Lo probé y descubrí que funcionaba.

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

cuando ejecuté mi comando foobar, funcionó exactamente como si hubiera escrito el comando a mano.


Absolutamente, el alias puede obtener parámetros, estoy usando este por ejemplo: alias commit = "git commit -m $ 1"
agapitocandemor

No me funciona: me alias myalias="echo 1 "$1" 3"; myalias 2da:1 3 2
dirdi

4

Existen 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 ejecutar 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. Entonces escribí algunas funciones para ese propósito. Sin embargo, hay ciertas variables que son (o pueden ser) cambiadas por una función llamada en sí. 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 definiciones 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 intérprete de comandos interactivo (o no en ninguna llamada de 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 se me ocurrió ha sido mencionada por otros en otras publicaciones sobre este tema. Para este comando específico para imprimir defns variables, 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 números arbitrarios de argumentos.

Una solución general para pasar argumentos arbitrarios a un comando con alias 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) de los comandos funcionarán al componer 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.


3

Una vez hice un proyecto divertido y todavía lo uso. Muestra algo de animación mientras copio archivos a través del cpcomando cpporque no muestra nada y es un poco frustrante. Entonces hice este alias

alias cp="~/SCR/spiner cp"

Y esta es la secuencia de comandos 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

Se parece a esto

ingrese la descripción de la imagen aquí

Animación cíclica)

ingrese la descripción de la imagen aquí


2

¡Para tomar parámetros, debe usar funciones!

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

Debe utilizar la función de shell en lugar de un alias para deshacerse de este problema. Puede 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úrate de agregar tu foo () a ~/.bash_profileo ~/.zshrcarchivo.

En tu caso, esto funcionará

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

No entiendo tu idea. Creo que no es importante cuando se interpretan los argumentos. Aliase acepta tantos parámetros como desee al final.
Timo

Pero es mejor hacerlo con funciones. Los alias no son para eso.
Ahmad Awais

1

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

Para completar y porque esto puede ser útil (sintaxis marginalmente más liviana), podría notarse que cuando los parámetros siguen el alias, todavía se pueden usar (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 esperaba como:ssh -O stop myhost

Esto puede ser útil para comandos que toman argumentos complejos (mi memoria ya no es lo que solía ser ...)


1

Tanto las funciones como los alias pueden usar parámetros como otros han mostrado aquí. Además, me gustaría señalar un par de otros aspectos:

1. la función se ejecuta en su propio alcance, el alias comparte el alcance

Puede ser útil saber esta diferencia en los casos en que necesite ocultar o exponer algo. También sugiere que una función es la mejor opción para la encapsulación.

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 

Salida:

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 de envoltura es una mejor opción

Me ha sucedido varias veces que no se puede encontrar un alias o una función al iniciar sesión a través dessh o al cambiar nombres de usuario o entornos multiusuario. Hay consejos y trucos con el abastecimiento de archivos de puntos, o este interesante con alias: alias sd='sudo 'permite que este alias posterior alias install='sd apt-get install'funcione como se espera (observe el espacio adicional en sd='sudo '). Sin embargo, un script de contenedor funciona mejor que una función o alias en casos como este. La principal ventaja de un script de envoltura es que es visible / ejecutable para la ruta prevista (es decir, / usr / loca / bin /) donde, como función / alias, debe obtenerse antes de que sea utilizable. Por ejemplo, coloca una función en un ~ / .bash_profile o ~ / .bashrc parabash , pero luego cambia a otro shell (es decirzsh) entonces la función ya no es visible. Por lo tanto, cuando tenga dudas, un script de contenedor siempre es la solución más confiable y portátil.


1
¿Por qué crees que la versión de la función no funciona?
tripleee

@tripleee. He editado la publicación para que sea más legible. Bajo el encabezado "1. alias funciona, función ..." ilustré por qué una versión de función no funciona. Y tal vez para responder la pregunta directamente, la función introduce un nuevo alcance donde el alias no lo hace.
biocyberman

Dices que no funciona pero no te creo. sourceen una función funciona bien.
tripleee

1
f () { source /tmp/nst; }hace exactamente lo que espero. Tal vez tu PATHestá mal, así que funciona mal activateo algo así; pero correr sourcedesde una función funciona bien.
tripleee

1
Por cierto, re: utilizando la functionpalabra clave en su definición, consulte wiki.bash-hackers.org/scripting/obsolete - function funcname {es una antigua sintaxis ksh que bash admite para la compatibilidad con versiones anteriores de shells POSIX, mientras que funcname() {es una sintaxis estandarizada oficial POSIX. function funcname() {es una mismah de las dos que no es compatible con ksh antiguo o compatible con POSIX sh, y que por lo tanto es mejor evitarlo.
Charles Duffy

1

Aquí está el ejemplo:

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

Muy importante:

  1. Hay un espacio después {y antes }.
  2. Hay un ;después de cada comando en secuencia. Si olvida esto después del último comando, verá un >mensaje en su lugar.
  3. El argumento está encerrado entre comillas como "$1"

2
No es necesario crear un alias para una función, solo use la función directamente.
user3439894

1

Como ya han señalado otros, el uso de una función debe considerarse la mejor práctica.

Sin embargo, aquí hay otro enfoque, aprovechando xargs:

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

Tenga en cuenta que esto tiene efectos secundarios con respecto a la redirección de secuencias.


0

No tiene que hacer nada, alias lo hace automáticamente.

Por ejemplo, si quiero hacer que el origen de git pull sea parametrizado, simplemente puedo crear un alias de la siguiente manera:

alias gpull = 'git pull origin '

y cuando realmente lo llama, puede pasar 'maestro' (el nombre de la sucursal) como un parámetro, como este:

gpull master
//or any other branch
gpull mybranch

Esto no responde la pregunta, ya que el argumento no se puede colocar libremente, sino solo al final.
dirdi
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.