Zrobić Alias Bash, który pobiera parametr?

Używałem CShell ( csh ), który pozwala na utworzenie aliasu, który pobiera parametr. Zapis był czymś w rodzaju

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

W Bash to nie działa. Biorąc pod uwagę, że Bash ma wiele przydatnych funkcji, zakładam, że ten został zaimplementowany, ale zastanawiam się, jak.

 907
Author: codeforester, 2011-08-20

12 answers

Alias Bash nie akceptuje bezpośrednio parametrów. Będziesz musiał utworzyć funkcję i alias, że.

alias nie akceptuje parametrów, ale funkcję można wywołać tak jak alias. Na przykład:

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


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

Nawiasem mówiąc, funkcje Bash zdefiniowane w .bashrc i inne pliki są dostępne jako polecenia w powłoce. Na przykład możesz wywołać wcześniejszą funkcję w ten sposób

$ myfunction original.conf my.conf
 1557
Author: arunkumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-12 16:02:49

Udoskonalając powyższą odpowiedź, można uzyskać składnię 1-liniową, jak można Dla aliasów, co jest wygodniejsze dla definicji ad-hoc w powłoce lub .pliki bashrc:

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

bash$ myfunction original.conf my.conf
Nie zapomnij o dwukropku przed zamknięciem prawego wspornika. Podobnie, dla rzeczywistego pytania:
csh% alias junk="mv \\!* ~/.Trash"

bash$ junk() { mv "$@" ~/.Trash/; }

Lub:

bash$ junk() { for item in "$@" ; do echo "Trashing: $item" ; mv "$item" ~/.Trash/; done; }
 145
Author: Mike Gleason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-14 16:10:13

Pytanie jest po prostu zadawane źle. Nie tworzysz aliasu, który pobiera parametry, ponieważ alias dodaje tylko drugą nazwę dla czegoś, co już istnieje. Funkcja, której chce OP, to polecenie function do utworzenia nowej funkcji. Nie musisz alias funkcji, ponieważ funkcja ma już nazwę.

Myślę, że chcesz coś takiego:

function trash() { mv "$@" ~/.Trash; }
To jest to! Możesz użyć parametrów $1, $2, $3 itp., lub po prostu wypchnąć je wszystkie za pomocą $@
 84
Author: Evan Langlois,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-10 05:11:12

TL;DR: zrób to zamiast

Jest o wiele łatwiejsze i bardziej czytelne użycie funkcji niż aliasu do umieszczania argumentów w środku polecenia.

$ wrap_args() { echo "before $@ after"; }
$ wrap_args 1 2 3
before 1 2 3 after

Jeśli czytasz dalej, dowiesz się rzeczy, których nie musisz wiedzieć o przetwarzaniu argumentów powłoki. Wiedza jest niebezpieczna. Po prostu uzyskaj pożądany rezultat, zanim ciemna strona na zawsze zapanuje nad twoim przeznaczeniem.

Wyjaśnienie

bash aliasy czy akceptują argumenty, ale tylko w end :

$ alias speak=echo
$ speak hello world
hello world

Umieszczanie argumentów wśrodku polecenia poprzez alias jest rzeczywiście możliwe, ale robi się nieprzyjemnie.

Nie próbujcie tego w domu, dzieciaki!

Jeśli lubisz omijać ograniczenia i robić to, co inni mówią, że jest niemożliwe, oto przepis. Tylko nie obwiniaj mnie, jeśli twoje włosy się postrzępią, a twoja twarz będzie pokryta sadzą w stylu szalonego naukowca.

Obejściem jest przekazanie argumentów, które alias akceptuje tylko na końcu do wrapper, który wstawia je w środku, a następnie wykonuje polecenie.

Rozwiązanie 1

Jeśli naprawdę jesteś przeciwny używaniu funkcji per se, możesz użyć:

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

Możesz zamienić $@ na $1 jeśli chcesz tylko pierwszy argument.

Wyjaśnienie 1

Tworzy to tymczasową funkcję f, która jest przekazywana argumentom (zauważ, że f jest wywoływana na samym końcu). unset -f usuwa definicję funkcji, ponieważ alias jest stracony, żeby potem nie zawisła.

Rozwiązanie 2

Możesz również użyć subshell:

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

Wyjaśnienie 2

Alias buduje polecenie w stylu:

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

Komentarze:

  • Symbol zastępczy _ jest wymagany, ale może to być cokolwiek. Jest on ustawiany na sh ' S $0 i jest wymagany, aby pierwszy z podanych przez użytkownika argumentów nie został zużyty. Demonstracja:

    sh -c 'echo Consumed: "$0" Printing: "$@"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
    
  • The pojedyncze cudzysłowy wewnątrz pojedynczych cudzysłowów są wymagane. Oto przykład tego, że nie działa z podwójnymi cudzysłowami: {]}

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

    Tutaj wartości powłoki interaktywnej $0 i {[11] } są zamieniane na podwójne cytowane , zanim zostanie przekazana do sh. Oto dowód:

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

    Pojedyncze cudzysłowy zapewniają, że te zmienne nie są interpretowane przez interaktywną powłokę i są przekazywane dosłownie do sh -c.

    Możesz użyć podwójnych cudzysłowów i \$@, ale najlepszą praktyką jest cytując twoje argumenty (ponieważ mogą zawierać spacje), i \"\$@\" wygląda jeszcze brzydiej, ale może pomóc Ci wygrać konkurs zaciemniania, w którym frazzled hair jest warunkiem wstępnym do wpisu.

 61
Author: Tom Hale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 03:02:51

Alternatywnym rozwiązaniem jest użycie marker , narzędzia, które niedawno stworzyłem, które pozwala na "zakładkę" szablonów poleceń i łatwe umieszczanie kursora w posiadaczach poleceń:

znacznik wiersza poleceń

Odkryłem, że przez większość czasu używam funkcji powłoki, więc nie muszę pisać często używanych poleceń w wierszu poleceń. Problem użycia funkcji w tym przypadku użycia polega na dodaniu nowych terminów do mojego słownika poleceń i konieczności zapamiętania, jakie parametry funkcji zobacz w poleceniu rzeczywistym. Celem markera jest wyeliminowanie tego obciążenia psychicznego.

 27
Author: Amine Hajyoussef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-22 13:24:24

Oto trzy przykłady funkcji, które mam w moim ~/.bashrc, które są zasadniczo aliasami akceptującymi parametr:

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

Bibliografia:

 7
Author: aliteralmind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:03

NB: W przypadku, gdy pomysł nie jest oczywisty, to jest złym pomysłem, aby używać aliasów do wszystkiego, oprócz aliasów, pierwszy z nich jest "funkcja w aliasie", a drugi jest "trudne do odczytania przekierowanie / źródło". Ponadto, są wady (które myślałem byłoby oczywiste, ale na wypadek, gdybyś był zdezorientowany: nie mam na myśli, aby były rzeczywiście używane... gdziekolwiek!)

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

Odpowiadałem już na to wcześniej i zawsze tak było w przeszłości:

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

Co jest dobre i dobre, chyba że unikasz korzystania z funkcji razem. w takim przypadku możesz skorzystać z ogromnej zdolności Basha do przekierowywania tekstu:

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

Obie są mniej więcej tej samej długości kilka znaków.

The real kicker jest różnicą czasu, góra jest 'metodą funkcji', a dół jest 'metodą redirect-source'. Aby udowodnić tę teorię, czas mówi sam za siebie:

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

Jest to dolna część około 200 wyników, wykonanych w losowych odstępach czasu. Wydaje się, że tworzenie/niszczenie funkcji zajmuje więcej czasu niż przekierowanie. Mam nadzieję, że pomoże to przyszłym odwiedzającym to pytanie (nie chciałem zachować tego dla siebie).

 4
Author: osirisgothra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-21 13:08:48

Do pobierania parametrów należy używać funkcji!

Jednak $ @ get interpretowane podczas tworzenia aliasu zamiast podczas wykonywania aliasu i ucieczki $ też nie działa. Jak rozwiązać ten problem?

Aby pozbyć się tego problemu, musisz użyć funkcji powłoki zamiast aliasu. Możesz zdefiniować foo w następujący sposób:

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

Lub

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

Na koniec wywołaj Foo () używając następującej składni:

foo arg1 arg2 argN

Upewnij się, że dodałeś foo () do ~/.bash_profile lub ~/.zshrc plik.

W Twoim przypadku to zadziała

function trash() { mv $@ ~/.Trash; }
 2
Author: Ahmad Awais,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 09:15:37

Istnieją uzasadnione powody techniczne, aby chcieć uogólnionego rozwiązania problemu aliasu bash, który nie ma mechanizmu do zmiany pozycji arbitralnych argumentów. Jednym z powodów jest to, że polecenie, które chcesz wykonać, będzie miało negatywny wpływ na zmiany w środowisku, które wynikają z wykonania funkcji. We wszystkich innych należy używać funkcji.

To, co ostatnio zmusiło mnie do próby rozwiązania tego problemu, to to, że chciałem stworzyć kilka skrótów polecenia do drukowania definicji zmiennych i funkcji. Więc napisałem kilka funkcji w tym celu. Istnieją jednak pewne zmienne, które są (lub mogą być) zmieniane przez samo wywołanie funkcji. Wśród nich są:

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

Podstawowe polecenie, którego używałem (w funkcji) do wypisywania zmiennych defns. w postaci wyjście poleceniem set było:

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

Np.:

> V=voodoo
sv V
V=voodoo

Problem: to nie wydrukuje definicje zmiennych wymienionych powyżej w bieżącym kontekście , np. jeśli w interaktywnym wierszu polecenia powłoki (lub nie w wywołaniu funkcji), FUNCNAME nie jest zdefiniowane. Ale moja funkcja mówi mi złe informacje:

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

Jedno rozwiązanie, które wymyśliłem, zostało wspomniane przez innych w innych postach na ten temat. Dla tego konkretnego polecenia wypisuje zmienne defns. i co wymaga tylko jednego argumentu, zrobiłem to:

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

Który daje prawidłowe wyjście (none), and result status (false):

> asv FUNCNAME
> echo $?
1

Jednak nadal czułem się zmuszony do znalezienia rozwiązania, które działa dla dowolnej liczby argumentów.

Ogólne Rozwiązanie Przekazywania Dowolnych Argumentów Do Polecenia Aliased 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".

Np.:

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

Miłą rzeczą w tym rozwiązaniu jest to, że wszystkie specjalne sztuczki używane do obsługi parametrów pozycyjnych (argumentów) poleceń będą działać podczas komponowania uwięzionego polecenia. Jedyną różnicą jest to, że należy użyć składni tablicy.

Np.,

Jeśli chcesz "$@", użyj " ${CMD_ARGV [@]}".

Jeśli chcesz "$#", użyj " ${#CMD_ARGV [@]}".

Itd.

 2
Author: crobc1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-18 17:42:09

Jeśli szukasz ogólnego sposobu na zastosowanie wszystkich paramów do funkcji, a nie tylko jednej, dwóch lub innej twardo zakodowanej kwoty, możesz to zrobić w ten sposób:

#!/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;

W powyższym przykładzie przekazuję wszystkie parametry od uruchomienia runjar do aliasu.

Na przykład, gdybym to zrobił {[2] } to skończyłoby się na uruchomieniu java -jar myjar.jar hi there. Gdybym to zrobił, to by działało java -jar myjar.jar one two three.

Podoba mi się to rozwiązanie oparte na $@, ponieważ działa z dowolną liczbą paramów.

 1
Author: Micah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-29 20:54:46

Funkcje są rzeczywiście prawie zawsze odpowiedzią, co zostało już potwierdzone cytatem ze strony podręcznika: " w prawie każdym celu aliasy są zastępowane przez funkcje powłoki."

Dla kompletności i ponieważ może to być użyteczne (nieco bardziej lekka składnia), można zauważyć, że Gdy parametr(y) podąża za aliasem, nadal mogą być używane (chociaż nie odpowiadałoby to wymaganiom OP). Jest to prawdopodobnie najłatwiejsze do wykazania za pomocą przykład:

alias ssh_disc='ssh -O stop'

Pozwala mi wpisać smth jak ssh_disc myhost, który jest rozszerzany zgodnie z oczekiwaniami jak: ssh -O stop myhost

Może to być przydatne dla poleceń, które przyjmują złożone argumenty(moja pamięć nie jest już tym, czego używa t be...)

 0
Author: sxc731,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-19 10:19:18

Nie potrzebujesz jednak funkcji podczas tworzenia aliasów w pliku .bashrc. Na przykład

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

Po dokonaniu zmiany w .plik bashrc, upewnij się, że wprowadzasz następujące polecenie.

~$ source .bashrc

Powinieneś być w stanie używać tego w ten sposób

~$ serve 8998
 -3
Author: kaizer1v,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-10 03:44:43