web-dev-qa-db-ja.com

パラメータを取るBashエイリアスを作成しますか?

私はCShell( csh )を使用していました。これにより、パラメータを取るエイリアスを作成できます。表記はこんな感じでした

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

Bashでは、これは機能しないようです。 Bashにはたくさんの便利な機能があることを考えれば、私はこれが実装されていると思いますが、どうやって疑問に思っていますか。

1040
Hello

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`

ところで、あなたの.bashrcや他のファイルで定義されたBash関数はあなたのシェルの中でコマンドとして利用可能です。だから例えばあなたはこのように以前の関数を呼び出すことができます

$ myfunction original.conf my.conf
1766
arunkumar

上記の答えを洗練すると、エイリアスの場合と同じように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; }
170
Mike Gleason

質問は単純に間違っています。 aliasはすでに存在しているものに2番目の名前を追加するだけなので、パラメータを取るエイリアスを作成しません。 OPが必要とする機能は、新しい関数を作成するためのfunctionコマンドです。関数にはすでに名前が付いているので、関数に別名を付ける必要はありません。

私はあなたがこのようなものが欲しいと思います:

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

それでおしまい! $ 1、$ 2、$ 3などのパラメータを使用することも、単に$ @ですべてを埋め込むこともできます。

94
Evan Langlois

TL; DR:代わりにこれをしてください

コマンドの途中に引数を入れるエイリアスよりも関数を使用する方がはるかに簡単で、読みやすくなっています。

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

あなたが読んだならば、あなたはあなたがShell引数処理について知る必要がないということを学ぶでしょう。知識は危険です。ダークサイドがあなたの運命を永遠にコントロールする前に、あなたが望む結果を得るだけです。

明確化

bashエイリアスdoは引数を受け取りますが、endでのみ有効です。

$ alias speak=echo
$ speak hello world
hello world

aliasを介してmiddle of commandに引数を入れることは確かに可能ですが、醜いことになります。

家でこれを試してはいけない、子供!

あなたが制限を回避し、他の人が言うことを行うことが不可能であることを好めば、ここにレシピがあります。あなたの髪がこわれて、あなたの顔がすすマッドサイエンティストスタイルで覆われてしまってしまったら私のせいにしないでください。

この問題を回避するには、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$@の値が二重引用符で囲まれたbeforeに置き換えられ、それがshに渡されます。これは証明です:

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

    一重引用符は、これらの変数が対話型シェルによって解釈されず、sh -cに文字通りに渡されることを保証します。

    二重引用符と\$@を使用することもできますが、ベストプラクティスは引数を引用符で囲み(スペースが含まれる可能性があるため)、\"\$@\"はさらに見栄えがよくなりますが.

82
Tom Hale

別の解決策は marker を使うことです。これは私が最近作成したツールで、コマンドテンプレートを "ブックマーク"にしてカーソルをコマンドプレースホルダーに簡単に置くことができます。

commandline marker

ほとんどの場合、私はシェル関数を使用しているので、頻繁に使用するコマンドをコマンドラインで何度も書く必要はありません。このユースケースで関数を使用することの問題は、私のコマンドの語彙に新しい用語を追加し、real-commandでどの関数のパラメータが参照するのかを覚えておく必要があることです。マーカーの目標は、その精神的な負担を取り除くことです。

32

これは私が~/.bashrcに持っている関数の3つの例です。それはパラメータを受け取る本質的にエイリアスです:

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

参考文献:

9
aliteralmind

NB:考えが明確でない場合、別名以外のものに別名を使用するのは悪い考えです。最初のものは「エイリアス内の関数」で、2番目のものは「読みにくいリダイレクト/ソース」です。また、欠陥もあります(i thoughtは明白ですが、念のために混乱しています。実際に使用されるわけではありません...どこにでもあります)。

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

私は前にこれに答えました、そして、それは過去においていつもこれのようでした:

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キッカーは時間差で、上が 'function method'、下が 'redirect-source'メソッドです。この理論を証明するために、タイミングはそれ自身のために話す:

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件の結果の下部です。関数の作成/破壊はリダイレクトよりも時間がかかるようです。うまくいけば、これはこの質問への将来の訪問者を助けるだろう(私自身にそれを維持したくない)。

6
osirisgothra

1つか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を実行するでしょう。

私はこの$@ベースの解決策が好きです。なぜなら、それはいくつものパラメータで動作するからです。

3
Micah

Bash別名の問題に対する一般化された解決策に、任意の引数を再配置するメカニズムを持たないことを望む正当な技術的理由があります。その理由の1つは、実行したいコマンドが、関数の実行から生じる環境への変更によって悪影響を受けることです。すべての other の場合には、関数を使うべきです。

最近これを解決することを強いられたのは、変数と関数の定義を表示するためのいくつかの省略形コマンドを作成したいということです。だから私はその目的のためにいくつかの関数を書きました。ただし、関数呼び出し自体によって変更される(または変更される可能性がある)特定の変数があります。その中には:

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

変数defnsを表示するために(関数内で)使用していた基本的なコマンド。 setコマンドの出力形式は次のとおりです。

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

例えば。:

> V=voodoo
sv V
V=voodoo

問題:これは 現在のコンテキスト にあるので上記の変数の定義を表示しません。 。しかし、私の関数は間違った情報を教えてくれます。

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

私が思いついた解決策の1つが、このトピックに関する他の投稿で他の人によって言及されました。変数defnsを出力するためのこの特定のコマンドで、引数を1つだけ必要とする場合は、次のようにしました。

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

これは正しい出力(none)と結果のステータス(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 [@]}"を使用してください。

等。

2
crobc1

パラメータを取るためには、関数を使うべきです!

ただし、$ @は、エイリアスの実行中ではなくエイリアスの作成時に解釈され、$のエスケープは機能しません。どうやってこの問題を解決しますか?

この問題を回避するには、エイリアスの代わりにシェル関数を使用する必要があります。次のようにfooを定義できます。

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

OR

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

最後に、次の構文を使ってfoo()を呼び出します。

foo arg1 arg2 argN

Foo()を~/.bash_profileまたは~/.zshrcファイルに追加してください。

あなたの場合、これはうまくいくでしょう

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

あなたがしなければならないのはエイリアスの中に関数を作ることだけです:

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

一重引用符は機能しないため、 must のように二重引用符を "$ 1"の前後に置いてください。

2
Ken Tran

他の人がここで言っているように、関数は通常良い選択ですが。しかし、私は指摘したいのですが、関数が機能しない場合や、エイリアスが機能する場合など、ラッパースクリプトの方が適している場合があります。

別名は機能しますが、機能は機能しません。また、パラメータを渡すこともできます。

例:私はcondaをアクティブにし、1つのコマンドで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 'の余分なスペースに注意してください。しかし、疑問がある場合は、ラッパースクリプトが常に最も信頼性が高く移植性の高いソリューションです。

1
biocyberman

Manページからのこの引用によってすでに十分に貢献され、確認されているように、実際には関数がほとんど常に答えです: 「ほとんどすべての目的のために、エイリアスはShell関数によって置き換えられます」。

完全性のために、そしてこれが有用であることができるので(わずかにより軽量の構文)、パラメータが別名に続くとき、それらはまだ使用されることができることに注意することができます(これはOPの要件に対処しません)。例を挙げて説明するのがおそらく最も簡単です。

alias ssh_disc='ssh -O stop'

ssh_disc myhostのようにsmthとタイプすることができます。これは期待通りに展開されます:ssh -O stop myhost

これは、複雑な引数を取るコマンドに便利です(私の記憶では、もう使用されていません...)。

0
sxc731