Pregunta Haga un alias de Bash que tome un parámetro?


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 ha sido implementada, pero me pregunto cómo.


866
2017-08-20 12:11


origen


Respuestas:


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 Bash definidas en su .bashrc y otros archivos están disponibles como comandos dentro de su caparazón. Entonces, por ejemplo, puede llamar a la función anterior como esta

$ myfunction original.conf my.conf

1500
2017-08-20 12:15



Refinando la respuesta anterior, puede obtener la 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 "$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; }

139
2018-05-14 15:46



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 la function comando 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 $ @


77
2018-01-08 06:35



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 hacer aceptar argumentos, pero solo en el fin:

$ alias speak=echo
$ speak hello world
hello world

Poniendo argumentos en el medio de comando a través de alias de hecho 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 es pasar los argumentos 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 -f f; }; f'
$ wrap_args x y z
before x y z after

Puedes reemplazar $@ con $1 si solo quieres el primer argumento

Explicación 1

Esto crea una función temporal f, que se pasa los argumentos (tenga en cuenta que f se llama al final). los 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 shes $0, y es necesario para que el primero de los argumentos dados por el usuario no se consuma. 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 del shell interactivo $0 y $@ son reemplazados en el doble citado antes de se pasa 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 tus 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 agotado es un requisito previo para la entrada.


58
2018-02-26 08:40



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

commandline marker

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 objetivo es eliminar esa carga mental.


26
2018-05-22 12:39



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


7
2018-01-13 03:57



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 yo pensamiento sería obvio, pero solo en caso de que esté confundido: ¡no me refiero a que sean realmente utilizados ... en ninguna parte!)

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

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.

los real 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).


4
2018-05-20 18:39