Сделать псевдоним Bash, который принимает параметр? - PullRequest
1078 голосов
/ 20 августа 2011

Я использовал CShell (), который позволяет создавать псевдоним, который принимает параметр.Запись была что-то вроде

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

В Bash, похоже, это не работает.Учитывая, что Bash обладает множеством полезных функций, я предполагаю, что эта функция была реализована, но мне интересно, как.

Ответы [ 13 ]

1817 голосов
/ 20 августа 2011

Псевдоним Bash напрямую не принимает параметры.Вам нужно будет создать функцию.

alias не принимает параметры, но функцию можно вызывать так же, как псевдоним.Например:

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


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

Кстати, функции Bash, определенные в вашем .bashrc и других файлах, доступны как команды в вашей оболочке.Так, например, вы можете вызвать более раннюю функцию, такую ​​как

$ myfunction original.conf my.conf
175 голосов
/ 14 мая 2014

Уточняя ответ выше, вы можете получить 1-строчный синтаксис, как вы можете для псевдонимов, что более удобно для специальных определений в оболочке или .bashrc файлах:забудьте точку с запятой перед закрывающей правой скобкой.Аналогично, для актуального вопроса:

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

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

Или:

bash$ junk() { for item in "$@" ; do echo "Trashing: $item" ; mv "$item" ~/.Trash/; done; }
97 голосов
/ 08 января 2016

Вопрос просто задан неправильно. Вы не создаете псевдоним, который принимает параметры, потому что alias просто добавляет второе имя для того, что уже существует. Функциональность, которую хочет OP, - это команда function для создания новой функции. Вам не нужно давать псевдоним функции, так как у функции уже есть имя.

Я думаю, вы хотите что-то вроде этого:

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

Вот и все! Вы можете использовать параметры $ 1, $ 2, $ 3 и т. Д. Или просто добавить их в $ @

87 голосов
/ 26 февраля 2017

TL; DR: Сделайте это вместо

Гораздо проще и удобочитаемее использовать функцию, чем псевдоним, чтобы поместить аргументы в середине команды.

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

Если вы продолжите читать, вы узнаете то, что вам не нужно знать об обработке аргументов оболочки.Знание опасно.Просто получите желаемый результат, прежде чем темная сторона навсегда контролирует вашу судьбу.

Разъяснение

bash псевдонимы do принимают аргументы, но только на end :

$ alias speak=echo
$ speak hello world
hello world

Ввод аргументов в middle команды с помощью alias действительно возможен, но становится уродливым.

Не пытайтесь сделать этодома, детки!

Если вам нравится обходить ограничения и делать то, что другие говорят, невозможно, вот рецепт.Только не вини меня, если твои волосы запутались, а твое лицо покрыто сажей в стиле безумного ученого.

Обходной путь - передать аргументы, которые alias принимает только в конце, оберткекоторый вставит их в середину и затем выполнит вашу команду.

Решение 1

Если вы действительно против использования функции как таковой, вы можете использовать:

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

Вы можете заменить $@ на $1, если вам нужен только первый аргумент.

Объяснение 1

Это создает временную функцию f, котораяпередаются аргументы (обратите внимание, что f вызывается в самом конце).unset -f удаляет определение функции при выполнении псевдонима, чтобы впоследствии он не зависал.

Решение 2

Вы также можете использовать подоболочку:

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

Объяснение 2

Псевдоним создает команду вроде:

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

Комментарии:

  • Заполнитель _ требуется, но это может быть что угодно.Он устанавливается на sh $0 и необходим для того, чтобы первый из заданных пользователем аргументов не использовался.Демонстрация:

    sh -c 'echo Consumed: "$0" Printing: "$@"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
    
  • Требуются одинарные кавычки внутри одинарных кавычек.Вот пример того, как он не работает с двойными кавычками:

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

    Здесь значения $0 и $@ интерактивной оболочки заменяются на двойные кавычки до того, как будет переданодо sh.Вот доказательство:

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

    Одинарные кавычки гарантируют, что эти переменные не интерпретируются интерактивной оболочкой, а передаются буквально sh -c.

    Вы можете использовать двойные кавычки и \$@, но лучше всего указывать свои аргументы (так как они могут содержать пробелы), и \"\$@\" выглядит еще страшнее, но может помочь вам выиграть конкурс на запутывание, где запутанные волосы являются необходимым условием для входа.

33 голосов
/ 22 мая 2015

Альтернативное решение заключается в использовании marker , инструмента, который я недавно создал, который позволяет вам «закладывать» шаблоны команд и легко размещать курсор на заполнителях команд:

commandline marker

Я обнаружил, что большую часть времени я использую функции оболочки, поэтому мне не нужно снова и снова писать часто используемые команды в командной строке. Проблема использования функций для этого варианта использования заключается в добавлении новых терминов в мой командный словарь и необходимости запоминать, на какие параметры функций ссылаются в реальной команде. Цель маркера - устранить это умственное бремя.

9 голосов
/ 13 января 2015

Вот три примера функций, которые у меня есть в моем ~/.bashrc, которые являются по существу псевдонимами, которые принимают параметр:

#Utility required by all below functions.
#/311526/kak-obrezat-probely-iz-peremennoi-bashcomment21953456_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
}

Ссылки:

6 голосов
/ 20 мая 2015

Примечание: если идея неочевидна, плохая идея использовать псевдонимы для чего угодно, кроме псевдонимов, первый из которых - «функция в псевдониме», а второй - «трудно читаемый редирект / источник'. Кроме того, есть недостатки (которые я считал очевидными, но на случай, если вы запутались: я не имею в виду, что они действительно будут использоваться ... где угодно!)

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

Я уже отвечал на это раньше, и в прошлом всегда было так:

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

это хорошо и хорошо, если только вы не избегаете использования функций вместе. в этом случае вы можете воспользоваться огромной способностью bash перенаправлять текст:

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

Они оба примерно одинаковой длины дают или принимают несколько символов.

Кикер real представляет собой разницу во времени, верхняя часть - «метод-функция», а нижняя - «метод перенаправления-источника». Чтобы доказать эту теорию, время говорит само за себя:

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

Это нижняя часть около 200 результатов, выполненных через случайные интервалы. Кажется, что создание / уничтожение функции занимает больше времени, чем перенаправление. Надеюсь, это поможет будущим посетителям в этом вопросе (не хотел оставлять его при себе).

4 голосов
/ 29 августа 2015

Если вы ищете общий способ применения всех параметров к функции, а не просто к одному, двум или другим жестко заданным значениям, вы можете сделать это следующим образом:

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

Так что вВ приведенном выше примере я передаю все параметры с момента запуска runjar псевдониму.

Например, если бы я выполнил runjar hi there, он бы действительно запустил java -jar myjar.jar hi there.Если бы я сделал runjar one two three, он бы запустил java -jar myjar.jar one two three.

Мне нравится это решение на основе $@, потому что оно работает с любым количеством параметров.

3 голосов
/ 10 апреля 2019

Все, что вам нужно сделать, это сделать функцию внутри псевдонима:

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

Вы должны поставить двойные кавычки вокруг "$ 1", потому что одинарные кавычки не будут работать.

2 голосов
/ 18 октября 2017

Существуют законные технические причины, чтобы хотеть обобщенного решения проблемы псевдонима bash, не имеющего механизма для принятия произвольных аргументов.Одна из причин заключается в том, что на команду, которую вы хотите выполнить, негативно повлияют изменения в среде, возникшие в результате выполнения функции.Во всех других случаях должны использоваться функции.

Что недавно заставило меня попытаться решить эту проблему, так это то, что я хотел создать несколько сокращенных команд для печати определений переменных и функций.Поэтому я написал несколько функций для этой цели.Однако существуют определенные переменные, которые (или могут быть) изменены самим вызовом функции.Среди них:

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

Основная команда, которую я использовал (в функции) для печати переменных defns.в форме, выведенной командой set, было:

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

Например:

> V=voodoo
sv V
V=voodoo

Проблема: Это не выведет определения переменных, упомянутых выше, как они есть в текущий контекст , например, если в приглашении интерактивной оболочки (или нет при вызове каких-либо функций), FUNCNAME не определено.Но моя функция сообщает мне неверную информацию:

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

Одно решение, которое я нашел, было упомянуто другими в других сообщениях на эту тему.Для этой конкретной команды для печати переменных defns., Которая требует только один аргумент, я сделал это:

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

, который дает правильный вывод (нет) и статус результата (false):

> asv FUNCNAME
> echo $?
1

Однако я все еще чувствовал необходимость найти решение, которое работает для произвольного числа аргументов.

Общее решение для передачи произвольных аргументов команде с псевдонимом 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".

Например:

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

Хорошая особенность этого решения заключается в том, что все специальные приемы, используемые для обработки позиционных параметров (аргументов) для команд, будут работать при составлении перехваченной команды.Единственное отличие состоит в том, что необходимо использовать синтаксис массива.

Например,

Если вы хотите использовать "$ @", используйте "$ {CMD_ARGV [@]}".

Если вы хотите "$ #", используйте "$ {# CMD_ARGV [@]}".

И т. Д.

...