Сделать псевдоним Bash, который принимает параметр?

995

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

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

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

  • 1
    Убедитесь, что вы используете кавычки вокруг аргументов "$1"
Показать ещё 2 комментария
Теги:
alias

13 ответов

1541
Лучший ответ

Атрибут 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
  • 4
    Спасибо! Я люблю это. Это позволило мне создать псевдоним для преобразования dos2unix для Perl на моем Mac. my_d2u() { tr -d '\r' < $1 > $2 } alias dos2unix=my_d2u; alias d2u=my_d2u;
  • 36
    Должен ли я заключить $1 в кавычки?
Показать ещё 17 комментариев
159

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

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

bash$ myfunction original.conf my.conf

Не забывайте точку с запятой перед закрывающей правой скобкой. Аналогично, для актуального вопроса:

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

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

Или же:

bash$ junk() { for item in "$@" ; do echo "Trashing: $item" ; mv "$item" ~/.Trash/; done; }
  • 3
    Я предпочитаю этот ответ, так как он показывает итерацию массива аргументов, которые могут прийти. $ 1 и $ 2 - это просто особые случаи, что также иллюстрируется в этом ответе.
  • 13
    Изменить mv "$1" "$1.bak"; cp "$2" "$1" в mv "$1" "$1.bak" && cp "$2" "$1" чтобы не потерять ваши данные, когда у mv проблемы (например, файловая система заполнена).
Показать ещё 5 комментариев
83

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

Я думаю, вам нужно что-то вроде этого:

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

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

  • 5
    Этот ответ говорит обо всем. Если бы я читал в нижней части этой страницы, я бы сэкономил время.
  • 0
    -1 Псевдоним и функция не эквивалентны ... 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
Показать ещё 4 комментария
66

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

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

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

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

Разъяснение

bash aliases принимают аргументы, но только в конце:

$ alias speak=echo
$ speak hello world
hello world

Ввод аргументов в середину команды через 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.

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

  • 1
    для решения 1 убедитесь, что вы используете одинарные кавычки, и избегайте \ "вокруг $ @. Очень полезная техника, если вам это нужно.
  • 0
    Решение 1 работало для меня, оберните rdesktop-vrdp аргументом для каждого сервера, который я хочу.
Показать ещё 2 комментария
29

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

Изображение 3879

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

7

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

#Utility required by all below functions.
#/questions/10090/how-to-trim-whitespace-from-a-bash-variable/67927#comment192496_67927
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: /questions/10092/in-bash-how-to-add-are-you-sure-yn-to-any-command-or-alias/67968#67968
    - Alias w/param: /questions/10073/make-a-bash-alias-that-takes-a-parameter/67804#67804
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 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.
    #- /questions/10091/how-do-i-add-a-line-break-for-read-command/67960#67960
    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 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
}

Литература:

  • 0
    А? Это функция, а не псевдоним. И одна из ваших ссылок - именно этот вопрос.
  • 0
    Это работает так же, как псевдоним для меня. Вам не нужно явное объявление псевдонима в этом случае.
Показать ещё 4 комментария
5

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

................................................................................................................................................р >

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

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

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

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

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

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

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 результатов, выполненных с произвольными интервалами. Кажется, что создание/уничтожение функции занимает больше времени, чем перенаправление. Надеюсь, это поможет будущим посетителям в этом вопросе (не хотел держать его в покое).

  • 1
    Иметь псевдоним, который определяет функцию, а затем запускать эту функцию просто глупо. Просто напишите функцию. Почти для любых целей псевдонимы заменяются функциями оболочки.
  • 0
    в случае, если вы не прочитали все, что вы можете попробовать, я иллюстрировал, почему использование метода функции было не так хорошо, во-первых, и, кроме того, это вовсе не глупо. Прекратите публиковать и понижать голосование только потому, что вам лично это не понравилось ... или, по крайней мере, дайте правильное объяснение. Обращение к имени - это первый признак близкого мышления ...
Показать ещё 4 комментария
2

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

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

имя_функции 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 [@]}".

Etc.

2

Для принятия параметров вы должны использовать функции!

Однако $@get интерпретируется при создании псевдонима, а не во время выполнения псевдонима и сбегания $doesnt. Как решить эту проблему?

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

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

ИЛИ

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

Наконец, вызовите ваш foo(), используя следующий синтаксис:

foo arg1 arg2 argN

Убедитесь, что вы добавили файл foo() в ~/.bash_profile или ~/.zshrc.

В вашем случае это будет работать

function trash() { mv $@ ~/.Trash; }
  • 0
    Я не понимаю вашу идею. Я думаю, что это не важно, когда аргументы интерпретируются. Алиас принимает столько параметров, сколько вы хотите в конце.
  • 0
    Но лучше сделать это с помощью функций. Псевдонимы не предназначены для этого.
2

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

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

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

  • 2
    почему бы просто не назвать функцию runjar а не сделать ее псевдонимом для функции? Кажется излишне сложным!
  • 0
    Функции @EvanLanglois автоматически называются alias (или имеют свойства вещей, передаваемых alias ) в файлах bash? если да, то это просто я не знал, что
Показать ещё 2 комментария
1

Хотя функция, как правило, здесь хороший выбор, как говорили другие. Но я хотел бы отметить, что есть случаи, когда функция не работает, а псевдоним делает, или другие случаи, сценарий-обертка - лучший выбор.

псевдоним работает, функция не работает, и все же можно передавать параметры.

Например: я хочу сделать ярлык для активации conda и создания среды conda одной командой. Было бы соблазн сделать это:

function conda_activate(){
    export PATH="$PATH:/path/to/conda"
    envname=$1
    if [ "x$envname" -ne "x" ]; then
       source activate "$envname"
    fi
}

Это не работает, как задумано. Если кто-то запустил conda_activate myenv. source команда выполняет поиск, но немедленно завершает работу, и у запущенной оболочки нет среды.

Рабочее решение это так:

function conda_activate(){
    export PATH="$PATH:/path/to/conda"
    # do some other things
}
alias conda_env='conda_activate; source activate'
# usage example
conda_env myenv

скрипт-обёртка - лучший выбор

Несколько раз мне случалось, что псевдоним или функция не могут быть найдены при входе в систему через ssh или при переключении имен пользователей или в многопользовательской среде. Существуют советы и рекомендации по работе с файлами точечных источников, например, этот интересный: alias sd='sudo ' позволяет использовать этот alias install='sd apt-get install' как и ожидалось. Обратите внимание на дополнительный пробел в sd='sudo '. Но когда вы сомневаетесь, скрипт-обертка всегда является наиболее надежным и переносимым решением.

0

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

Для полноты и потому, что это может быть полезно (незначительно более легкий синтаксис), можно отметить, что, когда параметр следуют за псевдонимом, они все равно могут использоваться (хотя это не будет касаться требования OP). Это, вероятно, проще всего продемонстрировать на примере:

alias ssh_disc='ssh -O stop'

позволяет мне вводить smth как ssh_disc myhost, который расширяется, как ожидалось, как: ssh -O stop myhost

Это может быть полезно для команд, которые принимают сложные аргументы (моя память больше не то, что она использует)...

-2

Однако при создании псевдонимов в файле .bashrc вам не нужны функции. Например

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

После внесения изменений в файл .bashrc убедитесь, что вы вводите следующую команду.

~$ source .bashrc

Вы должны иметь возможность использовать это так:

~$ serve 8998

Ещё вопросы

Сообщество Overcoder
Наверх
Меню