1773

I used to use CShell (), which lets you make an alias that takes a parameter. The notation was something like

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

In Bash, this does not seem to work. Given that Bash has a multitude of useful features, I would assume that this one has been implemented but I am wondering how.

codeforester
  • 39,467
  • 16
  • 112
  • 140
Hello
  • 17,827
  • 3
  • 16
  • 6
  • 3
    Make sure you use quotes around the args `"$1"` – Christophe Roussy Jul 15 '16 at 09:30
  • 1
    This question is off-topic for SO. It was [answered on UNIX.SE](https://unix.stackexchange.com/questions/3773/how-to-pass-parameters-to-an-alias), and the answer is that you don't even need to bother: "For instance, if you were to alias `ls` to `ls -la`, then typing `ls foo bar` would *really* execute `ls -la foo bar` on the command line." – Dan Dascalescu Oct 03 '17 at 06:32
  • 11
    that would not help with interpolating a variable into the middle of a string – Franz Sittampalam Apr 12 '18 at 16:09
  • 4
    Here's the lil test alias I used to discover this fallacy... `alias test_args="echo PREFIX --$1-- SUFFIX"`, which when called with `test_args ABCD` yields the following console output `PREFIX ---- SUFFIX ABCD` – jxramos Oct 10 '19 at 20:44
  • 5
    Note that since at least 1996, the bash documentation has contained the line: " For almost every purpose, aliases are superseded by shell functions", and I believe that is an understatement. There is absolutely no reason to ever use an alias instead of a function. – William Pursell Feb 13 '20 at 17:28
  • 2
    @WilliamPursell: "Absolutely no reason"? First, there are certain cases where a function cannot fulfill the role of an alias, eg. an alias ending in an open quote couldn't be done as a function (although you probably wouldn't want to anyway). More realistically, an alias ending in `do` would be impossible or impractical to write as a function. Secondly, for *all* aliases, you can conveniently expand the alias with `A-a` in ZSH or `C-A-e` in Bash (or whatever you configure), whereas there is no such functionality available for functions. – pyrocrasty Aug 06 '20 at 13:17
  • @pyrocrasty In bash, you can use tab expansion to expand function names. (Unsure about zsh). – William Pursell Aug 06 '20 at 13:39
  • @WilliamPursell: I don't mean name completion, I mean you can expand the alias name into its definition inline. (Try typing an alias and then `C-A-e` with the cursor directly after it) It's particularly handy when you can't remember a command's options but have an alias close to what you need. – pyrocrasty Aug 06 '20 at 13:56
  • @pyrocrasty That's a handy feature (if I used aliases!). For future readers, C-A-e is the default readline binding for the alias-expand-line function, which is not bound by default in vi mode. – William Pursell Aug 06 '20 at 14:16
  • 1
    I make extensive use of aliases that can not be made as functions, which are essential in all of my scripts. For example, this is a piece of code which adds an auto tracing mechanism in addition to decent exception handling: ```alias begin_function='trace=$((trace-1)); [ $trace -lt $bash_trace ] && set +x || set -x; local return_value=0; while true; do trace' alias end_function='break; done; [ $trace -ge $bash_trace ] && set -x; trace=$((trace+1))'``` – Jeremy Gurr Nov 24 '21 at 15:46

25 Answers25

2835

Bash alias does not directly accept parameters. You will have to create a function.

alias does not accept parameters but a function can be called just like an alias. For example:

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


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

By the way, Bash functions defined in your .bashrc and other files are available as commands within your shell. So for instance you can call the earlier function like this

$ myfunction original.conf my.conf
Aserre
  • 4,916
  • 5
  • 33
  • 56
arunkumar
  • 32,803
  • 4
  • 32
  • 47
  • 4
    Thanks! I love it. This allowed me to create an alias to convert dos2unix for perl on my mac. `my_d2u() { tr -d '\r' < $1 > $2 } alias dos2unix=my_d2u; alias d2u=my_d2u;` – Eradicatore Jun 07 '13 at 00:18
  • 71
    Should I wrap `$1` in quotes? – Jürgen Paul Jul 15 '13 at 06:43
  • 305
    You don't even have to declare the alias. Just defining the function will do. – dinigo Dec 10 '13 at 12:57
  • 295
    If you are changing an alias to a function, `source`ing your .bashrc will add the function but it won't unalias the old alias. Since aliases are higher precedent than functions, it will try to use the alias. You need to either close and reopen your shell, or else call `unalias `. Perhaps I'll save someone the 5 minutes I just wasted. – Marty Neal May 21 '14 at 17:25
  • 70
    @MartinNeal: One time-saving trick I learned at Sun is to just do an `exec bash`: It will start a new shell, giving you a clean read of your configs, just as if you closed and reopened, but keeping that session's environment variable settings too. Also, executing `bash` without the exec can be useful when you want to handle thinks like a stack. – Macneil Shonle Jul 09 '14 at 16:39
  • Thanks! BTW - just defining the function does the trick. There shouldn't be the need of defining the alias seperately – Arpit Apr 24 '15 at 09:40
  • How cool! function allows to eg get biggest folders and paths anywhere by entering the path as an argument: `duBiggests() { du -am "$1" | sort -nr | more }` – tuk0z Apr 29 '15 at 09:49
  • 43
    @mich - yes, always put bash variables in quotes. eg `mv "$1" "$1.bak"`. without quotes, if $1 were "hello world", you would execute `mv hello world hello world.bak` instead of `mv "hello world" "hello world.bak"`. – orion elenzil Jun 11 '15 at 16:17
  • 1
    We all work differently in the shell. I find it useful to alias my functions, like function fxx { find . |xargs grep $* 2>/dev/null; }, and then alias fx='fxx'. Then I can use 'which' and 'alias' to find my custom commands in case I forget. Easier than catting .bashrc (yes, I could make an alias for that...) – joe Feb 28 '16 at 20:09
  • `myname` and `myfunction` should always be different ? – Vicky Dev Jun 24 '16 at 10:23
  • How were you able to get away without prefixing your function with the declaration function: ( e.g. function myfunction() { do shell stuff here } ) – cgseller Aug 08 '17 at 18:52
  • 5
    if you put that in the `~/.bashrc` or something alike the functions name has to be prepended with `function`. For example `function myfunc{echo $1}` – SaAtomic Nov 07 '17 at 14:54
  • 1
    To have it in one line: myfunction() { mv "$1" "$1.bak"; cp "$2" "$1"; } – jaques-sam Jan 25 '18 at 13:17
  • 1
    using function docs/examples can be found in https://bash.cyberciti.biz/guide/Pass_arguments_into_a_function – yallam Jan 30 '18 at 09:25
  • 1
    this is combined with sourcing `.aliases` on zsh and bash is the cleanest answer ever! – Ishan Srivastava May 25 '18 at 17:11
  • 1
    Why do you call a myFunction inside the first chunk of code, but myfunction inside the second even though you seem to be referring to the same function? – Eduard Jun 10 '18 at 10:37
  • @agapitocandemor if you refer to the function 'alias' then, yes, of course it does. it is used to print or define aliases for the shell. if you talk about a defined alias, then no. the function alias defines a substitution of a word for a string if the word is used as the first word of a simple command. nothing more, nothing less. – BUFU Apr 30 '20 at 08:33
  • 1
    @BUFU you can see the other answers here, you can create an alias like `alias gpull = 'git pull origin "$1"'`, and it will work if you call it using `$ gpull master`. That's what I mean. – agapitocandemor Jun 05 '20 at 07:44
  • @agapitocandemor well, in that case, it will silently ignore that $1 is simply an empty string (since aliases do NOT accept arguments and very likely you do not have a variable named "1") and it just follows the replacement string `git pull origin` with whatever you put in behind it. It is a simple word-substitution. The other answers also make the same error. – BUFU Jun 07 '20 at 15:04
  • To instantiate an updated .bashrc, do the following `. ~/.bashrc` In this fashion, you don't have to start a new shell or kill the current session. I have the following alias to be able to tweak aliases on the fly: `alias epro='vi ~/.bashrc ;. ~/.bashrc'` – bagsmode Jul 22 '20 at 13:45
  • 1
    @Timo That's a rather bold statement. If you know of a way to answer the question with a solution which uses aliases, please do. (I suspect we will still continue to prefer and recommend functions.) – tripleee Oct 09 '21 at 18:16
  • @joe It's unclear why you find that useful. If you are still here, maybe post an answer or at least a comment to https://stackoverflow.com/questions/57239089/why-would-i-create-an-alias-which-creates-a-function – tripleee Oct 09 '21 at 18:18
304

Refining the answer above, you can get 1-line syntax like you can for aliases, which is more convenient for ad-hoc definitions in a shell or .bashrc files:

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

bash$ myfunction original.conf my.conf

Don't forget the semi-colon before the closing right-bracket. Similarly, for the actual question:

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

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

Or:

bash$ junk() { for item in "$@" ; do echo "Trashing: $item" ; mv "$item" ~/.Trash/; done; }
Mike Gleason
  • 3,439
  • 1
  • 14
  • 7
  • 5
    I prefer this answer, as it shows iterating through the array of arguments that might come in. $1 and $2 are just special cases, which is also illustrated in this answer. – philo vivero Sep 18 '14 at 22:04
  • 23
    Change `mv "$1" "$1.bak"; cp "$2" "$1"` into `mv "$1" "$1.bak" && cp "$2" "$1"` to not lose your data when `mv` runs into trouble (e.g., file system full). – Henk Langeveld Jan 23 '16 at 12:10
  • 9
    "Don't forget the semi-colon before the closing right-bracket." Many times this! Thanks :) – Kaushal Modi Feb 22 '18 at 17:53
  • 4
    And the spaces after the first bracket and before the last bracket are required as well. – Bryan Chapel Apr 17 '18 at 21:54
  • 1
    @HenkLangeveld While your remark is perfectly correct it shows that you have been around for a while -- I wouldn't know when I last encountered a "no space left on device" error ;-). (I use it regularily though when I don't find space on the kitchen counter any more!) Doesn't seem to happen very often these days. – Peter - Reinstate Monica Nov 15 '18 at 15:46
  • @PeterA.Schneider Well, `mv` and `cp` return status codes. Why have status codes and not use them? Who knows what else might go wrong? Shell programming is still systems programming. Fail safe. – Henk Langeveld Nov 15 '18 at 18:11
  • @HenkLangeveld Oh you are right by all accounts! I didn't mean to imply that one should ignore exist status. The "no space" thing just invoked memories. (The problems I face these days are more complex, like "When you enable acl handling in cygwin's fstab it screws up the acls such that whole directory trees become inaccessible for windows programs...".) – Peter - Reinstate Monica Nov 15 '18 at 18:31
  • I am using `tcsh` and I tried the `csh` script, I got `mv: No match` while sourcing the `.cshrc` file. – zyy Jan 11 '20 at 14:06
  • For what it's worth, there is no "above" or "below"; the answers on this page are sorted according to each visitor's preferences, and even then, many of them will produce different results at different points in time (if you sort by votes, the highest-voted answer could change, for example). – tripleee Nov 24 '22 at 05:35
207

The question is simply asked wrong. You don't make an alias that takes parameters because alias just adds a second name for something that already exists. The functionality the OP wants is the function command to create a new function. You do not need to alias the function as the function already has a name.

I think you want something like this :

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

That's it! You can use parameters $1, $2, $3, etc, or just stuff them all with $@

Evan Langlois
  • 4,050
  • 2
  • 20
  • 18
  • 19
    This answer says it all. If I'd read from the bottom of this page, I'd have saved some time. – joe Feb 28 '16 at 19:53
  • -1 An alias and a function are not equivalent... `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` – Fuzzy Logic May 09 '16 at 07:44
  • You really should quote `$@` to support file names with spaces, etc. – Tom Hale Mar 28 '17 at 06:26
  • It was supposed to be a general explanation that you don't have alias parameters, you use a function instead. The given function was just an example to counter the original example. I don't think the person was looking for a general purpose trash function. However, in the interest of presenting better code, I've updated the answer. – Evan Langlois Aug 10 '17 at 05:16
  • 2
    @FuzzyLogic - I spent a few minutes trying to unpack the code in your comment. That's a lot of (interesting) effort to go through to pack some code into a comment where you can't properly format it. I still haven't figured out exactly what it does or, more importantly, why it supports your (correct) assertion. – Joe Sep 25 '17 at 23:17
  • @Joe There was no effort except to make it run as a single line of code and to delete the test files. If you want to see what's going on just comment out the `rm -f ..` part at the end and it won't delete the test files. What it does is file2 defines an alias and a function that both read the name of the script path and then file2 is included from file1 and the alias and function are called from there. You can see from the output that they each produce a different result even though they are both defined in file2 and have the same code. The reason is because an alias is stored as a string. – Fuzzy Logic Sep 26 '17 at 03:55
  • 9
    "asked wrong"? That assumes the OP knew *exactly* what to ask, and if s/he did, there's a likelihood they wouldn't have asked in the first place. This is a forum to ask questions, hopefully clear good ones, but let's not be overly judgmental here. – Dan L May 02 '19 at 17:19
186

TL;DR: Do this instead

Its far easier and more readable to use a function than an alias to put arguments in the middle of a command.

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

If you read on, you'll learn things that you don't need to know about shell argument processing. Knowledge is dangerous. Just get the outcome you want, before the dark side forever controls your destiny.

Clarification

bash aliases do accept arguments, but only at the end:

$ alias speak=echo
$ speak hello world
hello world

Putting arguments into the middle of command via alias is indeed possible but it gets ugly.

Don't try this at home, kiddies!

If you like circumventing limitations and doing what others say is impossible, here's the recipe. Just don't blame me if your hair gets frazzled and your face ends up covered in soot mad-scientist-style.

The workaround is to pass the arguments that alias accepts only at the end to a wrapper that will insert them in the middle and then execute your command.

Solution 1

If you're really against using a function per se, you can use:

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

You can replace $@ with $1 if you only want the first argument.

Explanation 1

This creates a temporary function f, which is passed the arguments (note that f is called at the very end). The unset -f removes the function definition as the alias is executed so it doesn't hang around afterwards.

Solution 2

You can also use a subshell:

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

Explanation 2

The alias builds a command like:

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

Comments:

  • The placeholder _ is required, but it could be anything. It gets set to sh's $0, and is required so that the first of the user-given arguments don't get consumed. Demonstration:

    sh -c 'echo Consumed: "$0" Printing: "$@"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
    
  • The single-quotes inside single-quotes are required. Here's an example of it not working with double quotes:

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

    Here the values of the interactive shell's $0 and $@ are replaced into the double quoted before it is passed to sh. Here's proof:

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

    The single quotes ensure that these variables are not interpreted by interactive shell, and are passed literally to sh -c.

    You could use double-quotes and \$@, but best practice is to quote your arguments (as they may contain spaces), and \"\$@\" looks even uglier, but may help you win an obfuscation contest where frazzled hair is a prerequisite for entry.

Tom Hale
  • 40,825
  • 36
  • 187
  • 242
  • 3
    for solution 1, make sure you use single quotes, and avoid \" in around the $@. A very useful technique if you need it. ty. – sgtz Jun 27 '17 at 19:09
  • Solution 1 worked for me wrap rdesktop-vrdp with argument for each server I want. – Hsehdar May 04 '18 at 06:01
  • Accepting arguments at the end is exactly what I needed to replace "git checkout {branchname}" with a simple "gc {branchname}". In .bash_profile I simply had to add `alias gc='git checkout'` – AbstractVoid Mar 05 '19 at 11:47
  • @sgtz why would you want to remove quotes around `$@`? They are necessary if you have, eg, files with spaces in them. – Tom Hale Mar 11 '19 at 09:21
  • @TomHale This is somewhat linguistics, but solution 1 is not really useful if someone really is against using a function (whatever reason there may be) since you directly after admit yourself that you are only shifting the definition of a function to the execution time of the alias. secondly, "bash aliases do accept arguments, but only at the end" is more semantics: aliases are per definition substitutions of words by strings. they don't need to accept arguments, the commands that get executed via the substitution however can. alias is a character replacement, nothing more, nothing less. no? – BUFU Apr 30 '20 at 08:51
  • Perfect. I have a butt load of `/dev/loop` appearing in my standard `df` command. My alias now is: `alias df='f() { df "$@" | grep -v loop; }; f'` which auto-hides those loop devices from my default output. Having a function as in the other solution would for me to use a different name from the default Unix command... – Alexis Wilke May 22 '20 at 05:35
  • @alexis-wilke why would you need to use a different name?? If this is the behaviour you want for all uses of df, just call your function df... – BUFU Jun 07 '20 at 15:09
  • @BUFU, There are a few times when I'd want to see the loops. But now I know that I could simply use the full path (`/usr/bin/df`) and that ignores the function/alias altogether. So either way works. – Alexis Wilke Jun 07 '20 at 17:53
  • 1
    @AlexisWilke exacty. and even easier, you can simply use `\df` – BUFU Jun 08 '20 at 20:06
  • Tom, I like you: "Knowledge is dangerous.." – Timo Jun 06 '21 at 11:09
81

All you have to do is make a function inside an alias:

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

You must put double quotes around "$1" because single quotes will not work. This is because clashing the quotes at the places marked with arrows confuses the system. Also, a space at the place marked with a star is needed for the function.

CauseYNot
  • 1,088
  • 7
  • 15
  • 10
    That's clever, but other than the fact that the question did specify to make an alias, is there any reason to not just make the function with the same name as the alias initially? – xaxxon May 16 '19 at 18:03
  • 2
    @xaxxon Not really, but it's the easiest way I know to use an alias, not a function. – CauseYNot Aug 04 '19 at 12:06
  • 1
    This does not work. On Ubuntu 18, I'm getting error `bash: syntax error near unexpected token '{mkdir'`. – Shital Shah Jan 22 '20 at 00:21
  • missing a trailing `;` inside the function `_mkcd` most probably the error is from that. – Jetchisel Jan 22 '20 at 00:45
  • Leave space before and after the `{` and `}` – hesham_EE Jan 22 '20 at 15:23
  • 2
    I've fixed the syntax issues. Re: removing the `function` prefix, see discussion in https://wiki.bash-hackers.org/scripting/obsolete. That said, I don't know why anyone would do this in preference to only defining a function and not using any alias at all. – Charles Duffy Feb 12 '20 at 23:27
  • You can escape/disable alias by `\`, eg. `\ls`, but if you change `ls` to be a function (eg. `ls() { /bin/ls -l; }`) then `\ls` is still using function in place of just `ls`. – faramir Mar 11 '20 at 14:46
  • 1
    @faramir you can use `command`. you could even use an alias like `alias c = 'command'` and then simply use `c ls`. – BUFU Jul 08 '20 at 12:56
  • @ShitalShah you are missing a space between '{' and 'm' – CauseYNot Oct 27 '20 at 13:54
  • 1
    [Why would I create an alias which creates a function?](https://stackoverflow.com/questions/57239089/why-would-i-create-an-alias-which-creates-a-function) Probably don't. – tripleee Nov 24 '22 at 05:31
51

Once I did some fun project and I'm still using it. It's showing some animation while copy files via cp command coz cp don't show anything and it's kind of frustrating. So I've made this alias for cp:

alias cp="~/SCR/spinner cp"

And this is the spinner script

#!/bin/bash

#Set timer
T=$(date +%s)

#Add some color
. ~/SCR/color

#Animation sprites
sprite=( "(* )  ( *)" " (* )( *) " " ( *)(* ) " "( *)  (* )" "(* )  ( *)" )

#Print empty line and hide cursor
printf "\n${COF}"

#Exit function
function bye { printf "${CON}"; [ -e /proc/$pid ] && kill -9 $pid; exit; }; trap bye INT

#Run our command and get its pid
"$@" & pid=$!

#Waiting animation
i=0; while [ -e /proc/$pid ]; do sleep 0.1

    printf "\r${GRN}Please wait... ${YLW}${sprite[$i]}${DEF}"
    ((i++)); [[ $i = ${#sprite[@]} ]] && i=0

done

#Print time and exit
T=$(($(date +%s)-$T))
printf "\n\nTime taken: $(date -u -d @${T} +'%T')\n"

bye

It looks like this

enter image description here

Cycled animation)

enter image description here

Here is the link to a color script mentioned above. And new animation cycle)

enter image description here

So the answer to the OP's question is to use intermediate script that could shuffle args as you wish.

Ivan
  • 6,188
  • 1
  • 16
  • 23
43

An alternative solution is to use marker, a tool I've created recently that allows you to "bookmark" command templates and easily place cursor at command place-holders:

commandline marker

I found that most of time, I'm using shell functions so I don't have to write frequently used commands again and again in the command-line. The issue of using functions for this use case, is adding new terms to my command vocabulary and having to remember what functions parameters refer to in the real-command. Marker goal is to eliminate that mental burden.

Amine Hajyoussef
  • 4,381
  • 3
  • 22
  • 26
24

Syntax:

alias shortName="your custom command here"

Example:

alias tlogs='_t_logs() { tail -f ../path/$1/to/project/logs.txt ;}; _t_logs'
סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
Maxx Selva K
  • 454
  • 4
  • 9
  • Really clever solution! If you try this out and just get a `>` prompt, make sure you got the `;` before the `}`. Also note this redefines the function every time you use the alias, so if the function definition is expensive, it could be better to move it out of the `alias`. – aschmied Sep 18 '21 at 12:16
  • 2
    Why would you do this instead of just defining a function and not having any alias at all? – Charles Duffy Sep 19 '21 at 16:52
  • I used this idea for this alias to colorize json output from kubectl. Thanks! `alias kj='_kc_jq() { kc get -o json $@ |jq; }; _kc_jq'` – Josiah Mar 09 '22 at 20:36
  • Besides being crazy, this duplicates Ken Tran's answer from 2019. – tripleee Nov 24 '22 at 05:31
17

Bash alias absolutely does accept parameters. I just added an alias to create a new react app which accepts the app name as a parameter. Here's my process:

Open the bash_profile for editing in nano

nano /.bash_profile

Add your aliases, one per line:

alias gita='git add .'
alias gitc='git commit -m "$@"'
alias gitpom='git push origin master'
alias creact='npx create-react-app "$@"'

note: the "$@" accepts parameters passed in like "creact my-new-app"

Save and exit nano editor

ctrl+o to to write (hit enter); ctrl+x to exit

Tell terminal to use the new aliases in .bash_profile

source /.bash_profile

That's it! You can now use your new aliases

Billeh
  • 1,257
  • 7
  • 6
  • 9
    This doesn't seem to work if the parameters are in the middle of the command. I suspect what is happening is you're calling the alias followed by the "parameters" which just happen to be at the end of your command so it's interpreted as if your parameter at the end is at the end. e.g. `gitc foo` runs `git commit -m foo` not because you have a $@ but because foo came at the end -- it's running `git commit -m` and you happened to have a `foo` that followed and was appended and that made your command valid. – shufler Sep 20 '20 at 02:03
  • 8
    Summing up, aliases accept parameters, but only when put to the end of the alias.This is kind of limited parameters. And you DO NOT NEED the `$@` in an alias def. – Timo Oct 21 '20 at 11:45
  • You can remove all positional variable references from your aliases and it will still work—all those placeholder are replaced with an empty string and parameters given to alias invocation are simply appended to the expansion of the alias, at its end. So you may think it works the way you described, but in fact it doesn't. You will learn that as only you put placeholder in the middle or at the beginning of an alias. – Cromax Sep 17 '21 at 15:14
  • If you run `set -- "first argument" "second argument"` before calling your aliases, all the `"$@"`s will turn into `"first argument" "second argument"`s. They don't reflect the parameters passed to your alias; they only reflect the parameters that are _active inside the context in which the alias is called_. – Charles Duffy Sep 19 '21 at 17:01
14

Here's are three examples of functions I have in my ~/.bashrc, that are essentially aliases that accept a parameter:

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

References:

Community
  • 1
  • 1
aliteralmind
  • 19,847
  • 17
  • 77
  • 108
  • Huh? That's a function, not an alias. And one of your references is this very question. – tripleee Jan 13 '15 at 04:59
  • It works exactly like an alias for me. You don't need the explicit alias declaration in this case. – aliteralmind Jan 13 '15 at 05:00
  • 2
    No, it works exactly like a function. Like several of the answers here explain, you cannot make an alias which takes a parameter. What you can do is write a function, which is what you have done. – tripleee Jan 13 '15 at 05:01
  • 1
    @tripleee From my bash newbie point of view, before reading your comment, I thought it was an alias (an alternative way to create one). It functions exactly like one, as far I can tell. It is *essentially* an alias that accepts a parameter, even if I am off on the terminology. I don't see the problem. I've clarified the link to the other answer in this problem. It helped me create this. – aliteralmind Jan 13 '15 at 05:04
  • 1
    Perhaps this doesn't specifically answer the question of "a bash alias that accepts a parameter", because I understand now that it's not possible, but it certainly *effectively* answers it. What am I missing here? – aliteralmind Jan 13 '15 at 05:23
  • 2
    Some really good examples here, and nicely commented code. A great help for the people coming to this page. – chim Mar 25 '15 at 12:09
14

Respectfully to all those saying you can't insert a parameter in the middle of an alias I just tested it and found that it did work.

alias mycommand = "python3 "$1" script.py --folderoutput RESULTS/"

when I then ran mycommand foobar it worked exactly as if I had typed the command out longhand.

ctf0
  • 6,991
  • 5
  • 37
  • 46
Nathaniel
  • 307
  • 2
  • 9
  • 1
    Absolutely, alias can get parameters, I'm ages using this one e.g.: alias commit="git commit -m $1" – agapitocandemor Mar 28 '20 at 09:12
  • 4
    Does not work for me: ```alias myalias="echo 1 "$1" 3"; myalias 2``` gives me: ```1 3 2``` – dirdi May 20 '20 at 23:20
  • 4
    "There is no mechanism for using arguments in the replacement text, as in csh" https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Aliases Your command probably works because *appending* whatever you put in $1 at the end of that cmd line still works for whatever you're doing. – Fizz Jun 26 '20 at 09:07
  • You can expand aliases inline to convince yourself https://superuser.com/a/247781/345617 – Fizz Jun 26 '20 at 09:16
  • 1
    Will work the same as: `alias mycommand = "python3 script.py --folderoutput RESULTS/"` (with omitting `"$1"`). It's the same because alias does not set positional parameters in bash. In an interactive shell, their value is "" an empty string, unless you put something in there. To further test that, try really adding something to $1: `set -- something` before defining alias. now your alias is very possibly broken, see what that alias contains: `alias mycommand` – papo Nov 25 '20 at 10:51
  • 1
    @agapitocandemor If your comment is not a joke (it's not that apparent…), then your alias is broken and it “works” only because `$1` gets substituted with empty string and your comment gets appended. You can safely redefine your alias to `git commit -m` and it will still work. – Cromax Sep 17 '21 at 15:06
  • 1
    @Nathaniel, the `$1` in an alias isn't the argument passed to the alias, it's an argument passed to the script, shell, or function in which the alias is expanded. So it "works", but not in a useful way. You can test this trivially. `set -- "first argument" "second argument"; alias testAlias='echo "$1"; # ''; testAlias "Other String"` ignores `Other String` and echos `first argument`. – Charles Duffy Sep 19 '21 at 16:53
  • With the spaces around `=` this isn't even valid Bash syntax. I won't repeat the earlier comments about why this doesn't actually work even if you fix that. – tripleee Nov 24 '22 at 05:30
11

NB: In case the idea isn't obvious, it is a bad idea to use aliases for anything but aliases, the first one being the 'function in an alias' and the second one being the 'hard to read redirect/source'. Also, there are flaws (which i thought would be obvious, but just in case you are confused: I do not mean them to actually be used... anywhere!)


I've answered this before, and it has always been like this in the past:

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

which is fine and good, unless you are avoiding the use of functions all together. in which case you can take advantage of bash's vast ability to redirect text:

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

They are both about the same length give or take a few characters.

The real kicker is the time difference, the top being the 'function method' and the bottom being the 'redirect-source' method. To prove this theory, the timing speaks for itself:

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

This is the bottom part of about 200 results, done at random intervals. It seems that function creation/destruction takes more time than redirection. Hopefully this will help future visitors to this question (didn't want to keep it to myself).

tripleee
  • 175,061
  • 34
  • 275
  • 318
osirisgothra
  • 2,163
  • 24
  • 19
  • 4
    Having an alias that defines a function, then run that function is just silly. Just write a function. For almost every purpose, aliases are superseded by shell functions. – geirha May 20 '15 at 18:50
  • in case you didnt read the whole thing, which you might try, i was illustrating why using the function method was no at as good in the first place, and furthermore, it isn't silly at all. Stop posting and downvoting just because you didn't personally like it... or at least give a valid explanation. Name calling is the first sign close mindedness... – osirisgothra May 20 '15 at 19:03
  • 1
    The second one (the bar alias) has several side effects. Firstly the command can't use stdin. Secondly, if no arguments are provided after the alias, whatever arguments the shell happens to have gets passed instead. Using a function avoids all these side effects. (Also useless use of cat). – geirha May 20 '15 at 19:31
  • 2
    hey i never said that it was good idea to do any of this, i just said there was also other ways to go about it -- the main idea here is that you should not use functions in aliases because they are slower even than a complicated redirect, I thought that would have been obvious but I guess I have to spell it out for everyone (which I also get yelled at for the post being too bloated if I did that in the first place) – osirisgothra May 21 '15 at 12:55
  • 1
    Since the timings of _bar_ are all 0's, I would suspect that the shell isn't timing this correctly. Notice that the time message is printed **before** the command is run? Ergo, it times nothing and then performs bar, so you aren't measuring bar at all. Do something more complex, or a big loop in bar so you can make it more obvious that you are timing **nothing** – Evan Langlois Dec 21 '15 at 18:41
  • 1
    I now tried this (though I had to replace the final __foo() by __foo). `time for i in {1..1000}; do foo FOOVALUE; done` → 0m0.028s. But `time for i in {1..1000}; do bar FOOVALUE; done` → 0m2.739s. **Bar is two orders of magnitude slower** than foo. Just using a plain function instead of an alias reduces runtime by another 30%: `function boo() { echo "arg1 for boo=$1" ; }` ⇒ `time for i in {1..1000}; do boo FOOVALUE; done` → 0m0.019s. – Arne Babenhauserheide Dec 01 '16 at 16:31
9

If you're looking for a generic way to apply all params to a function, not just one or two or some other hardcoded amount, you can do that this way:

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

So in the example above, i pass all parameters from when i run runjar to the alias.

For example, if i did runjar hi there it would end up actually running java -jar myjar.jar hi there. If i did runjar one two three it would run java -jar myjar.jar one two three.

I like this $@ - based solution because it works with any number of params.

Micah
  • 10,295
  • 13
  • 66
  • 95
  • 4
    why not just name the function `runjar` instead of making it an alias to a function? Seems needlessly complicated! – Evan Langlois Dec 21 '15 at 18:34
  • @EvanLanglois functions are automatically `alias`-ed (or having the properties of things passed to `alias`) in bash files? if yes, then its just my not having known that – Micah Dec 22 '15 at 12:58
  • Don't understand what you mean. An alias is another name, be it a person or a function. So, you made a function, then you made a second name for the function. There is nothing special about the alias, its just a second name and not required. What you type at the command line can be internal or external functions, so "function" makes a new command, not alias. – Evan Langlois Jan 08 '16 at 06:29
  • 5
    This is pointless. It's the same as `alias runjar='java -jar myjar.jar'`. Aliases do accept arguments, but only at the end. – Tom Hale Feb 26 '17 at 07:45
6

There are legitimate technical reasons to want a generalized solution to the problem of bash alias not having a mechanism to take a reposition arbitrary arguments. One reason is if the command you wish to execute would be adversely affected by the changes to the environment that result from executing a function. In all other cases, functions should be used.

What recently compelled me to attempt a solution to this is that I wanted to create some abbreviated commands for printing the definitions of variables and functions. So I wrote some functions for that purpose. However, there are certain variables which are (or may be) changed by a function call itself. Among them are:

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

The basic command I had been using (in a function) to print variable defns. in the form output by the set command was:

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

E.g.:

> V=voodoo
sv V
V=voodoo

Problem: This won't print the definitions of the variables mentioned above as they are in the current context, e.g., if in an interactive shell prompt (or not in any function calls), FUNCNAME isn't defined. But my function tells me the wrong information:

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

One solution I came up with has been mentioned by others in other posts on this topic. For this specific command to print variable defns., and which requires only one argument, I did this:

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

Which gives the correct output (none), and result status (false):

> asv FUNCNAME
> echo $?
1

However, I still felt compelled to find a solution that works for arbitrary numbers of arguments.

A General Solution To Passing Arbitrary Arguments To A Bash Aliased Command:

# (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".

E.g.:

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

A nice thing about this solution is that all the special tricks used to handle positional parameters (arguments) to commands will work when composing the trapped command. The only difference is that array syntax must be used.

E.g.,

If you want "$@", use "${CMD_ARGV[@]}".

If you want "$#", use "${#CMD_ARGV[@]}".

Etc.

crobc1
  • 161
  • 2
  • 4
6

I will just post my (hopefully, okay) solution (for future readers, & most vitally; editors).
So - please edit & improve/remove anything in this post.

In the terminal:

$ alias <name_of_your_alias>_$argname="<command> $argname"

and to use it (notice the space after '_':

$<name_of_your_alias>_ $argname

for example, a alias to cat a file called hello.txt:

  • (alias name is CAT_FILE_)
  • and the $f (is the $argname, which is a file in this example)
$ alias CAT_FILE_$f="cat $f"

$ echo " " >> hello.txt
$ echo "hello there!" >> hello.txt
$ echo " " >> hello.txt
$ cat hello.txt

    hello there!

Test (notice the space after '_'):

CAT_FILE_ hello.txt
shwz
  • 426
  • 1
  • 6
  • 22
William Martens
  • 753
  • 1
  • 8
  • 27
  • 1
    When passing an argument, a space is needed before the argument – shwz Aug 25 '21 at 08:33
  • 6
    This works but for a completely different reason. It works only because `$f` is at the end. The `$f` is probably empty at alias definition time and therefore resolves into `alias CAT_FILE_="cat "`. Just run `alias` to see the resulting definition. So when you call `CAT_FILE_` you just pass the argument to the alias as in any other alias usage like in `alias ll="ls -l"`. Try to put the "argument" `$f` somewhere else than at the end and this won't work. E.g: `alias ECHO_ARG_$f="echo xxx $f yyy"`. Then `ECHO_ARG_ test` leads to `xxx yyy test`. – Holger Böhnke Nov 23 '21 at 10:09
5

Solution with subcommands:

d () {
    if [ $# -eq 0 ] ; then
        docker
        return 0
    fi
    CMD=$1
    shift

    case $CMD in
    p)
        docker ps --all $@
        ;;
    r)
        docker run --interactive --tty $@
        ;;
    rma)
        docker container prune
        docker image prune --filter "dangling=true"
        ;;
    *)
        docker $CMD $@
        ;;
    esac
    return $?
}

Using:

$ d r my_image ...

Called:

docker run --interactive --tty my_image ...
Anton Abrosimov
  • 349
  • 4
  • 6
4

As has already been pointed out by others, using a function should be considered best practice.

However, here is another approach, leveraging xargs:

alias junk="xargs -I "{}" -- mv "{}" "~/.Trash" <<< "

Note that this has side effects regarding redirection of streams.

dirdi
  • 267
  • 1
  • 14
  • What do you think the inner quotes are doing? (They __do not__ become part of the alias; if you wanted them to, you'd need to escape them: `alias junk=xargs -I \"{}\" -- mv \"{}\" \"~/.Trash\" <<<"`). – Charles Duffy Sep 19 '21 at 16:57
  • That said, you don't actually want those quotes, because `xargs` isn't doing textual substitution (it operates on the array of C strings generated after shell parsing is already over, so quotes no longer have value wrt changing shell behavior), and when quoted `~` doesn't work. So this would be better as just `alias junk='xargs -I {} -- mv {} ~/.Trash <<<'` – Charles Duffy Sep 19 '21 at 16:59
  • @CharlesDuffy feel free to edit and improve my answer. – dirdi Sep 20 '21 at 18:25
3

Here's the example:

alias gcommit='function _f() { git add -A; git commit -m "$1"; } ; _f'

Very important:

  1. There is a space after { and before }.
  2. There is a ; after each command in sequence. If you forget this after the last command, you will see > prompt instead!
  3. The argument is enclosed in quotes as "$1"
Shital Shah
  • 63,284
  • 17
  • 238
  • 185
3

To give specific answer to the Question posed about creating the alias to move the files to Trash folder instead of deleting them:

alias rm="mv "$1" -t ~/.Trash/"

Offcourse you have to create dir ~/.Trash first.

Then just give following command:

$rm <filename>
$rm <dirname>
Neeraj Jain
  • 598
  • 10
  • 10
  • 4
    Or even better and with the same results: `alias rm="mv "$i_bellieve_this_is_correct_because_it_WORKS" -t ~/.Trash/"` Who is still not convinced can run this command before: `set -- my_very_important_file` Run it literally or use path to your very important file if you feel confident $1 in alias works. now statement: `alias rm="mv "$1" -t ~/.Trash/"` and `rm ` as suggested. `set --` sets value to positional parameter $1. alias command does not. – papo Nov 25 '20 at 10:45
  • As papo says above: This only looks like it works because even if `$1` is completely empty, `mv` still honors arguments in trailing position. The `$1` is **not** populated by the arguments passed to your alias; it's populated from the `$1` value that was preexisting in the shell in which the alias was run. – Charles Duffy Sep 19 '21 at 16:56
3

Here is another approach using read. I am using this for brute search of a file by its name fragment, ignoring the "permission denied" messages.

alias loc0='( IFS= read -r x; find . -iname "*" -print 2>/dev/null | grep $x;) <<<'

A simple example:

$ ( IFS= read -r x; echo "1 $x 2 ";) <<< "a b"
1 a b 2 

Note, that this converts the argument as a string into variable(s). One could use several parameters within quotes for this, space separated:

$ ( read -r x0 x1; echo "1 ${x0} 2 ${x1} 3 ";) <<< "a b"
1 a 2 b 3 
elomage
  • 4,334
  • 2
  • 27
  • 23
  • I think the `{` should be `(` instead. – Maximilian Ballard Dec 04 '22 at 08:38
  • @MaximilianBallard Parentheses cause the commands to be run in a subshell. With braces they will run in the same shell. See here for more: https://unix.stackexchange.com/a/267661/59988 – elomage Dec 05 '22 at 09:21
  • 1
    Yes, but if you don't use a subshell then the variables will be saved to environment so if you reuse alias it can cause issues, or if you reuse var names... Using a subshell prevents that. – Maximilian Ballard Dec 05 '22 at 12:22
  • @MaximilianBallard Good point about the variables. Let's change to "( ": safety before efficiency. – elomage Dec 06 '22 at 15:05
3

You can create an anonymous function within your alias:

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

You need " around $1 to handle names with spaces in them. Outer ' avoid the need for escaping " and $. You could also use all " with a bunch of escaping:

alias mkcd="(){mkdir \"\$1\";cd \"\$1\";}"

I think this is cleaner than the multiple other answers that create a named function.

Either of these have the same usage. mkcd foo will mkdir "foo" followed by cd "foo"

Sparr
  • 7,489
  • 31
  • 48
2

Functions are indeed almost always the answer as already amply contributed and confirmed by this quote from the man page: "For almost every purpose, aliases are superseded by shell functions."

For completeness and because this can be useful (marginally more lightweight syntax) it could be noted that when the parameter(s) follow the alias, they can still be used (although this wouldn't address the OP's requirement). This is probably easiest to demonstrate with an example:

alias ssh_disc='ssh -O stop'

allows me to type smth like ssh_disc myhost, which gets expanded as expected as: ssh -O stop myhost

This can be useful for commands which take complex arguments (my memory isn't what it use t be anymore...)

sxc731
  • 2,418
  • 2
  • 28
  • 30
2

For taking parameters, you should use functions!

However $@ get interpreted when creating the alias instead of during the execution of the alias and escaping the $ doesn’t work either. How do I solve this problem?

You need to use shell function instead of an alias to get rid of this problem. You can define foo as follows:

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

OR

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

Finally, call your foo() using the following syntax:

foo arg1 arg2 argN

Make sure you add your foo() to ~/.bash_profile or ~/.zshrc file.

In your case, this will work

function trash() { mv $@ ~/.Trash; }
Ahmad Awais
  • 33,440
  • 5
  • 74
  • 56
  • I do not understand your idea. I think it is not important when the arguments get interpreted. Aliase accept as much parameters as you want at the end. – Timo Oct 18 '17 at 19:41
  • But it's better to do that with functions. Aliases are not meant for that. – Ahmad Awais Oct 18 '17 at 20:21
1

Both functions and aliases can use parameters as others have shown here. Additionally, I would like to point out a couple of other aspects:

1. function runs in its own scope, alias shares scope

It may be useful to know this difference in cases you need to hide or expose something. It also suggests that a function is the better choice for encapsulation.

function tfunc(){
    GlobalFromFunc="Global From Func" # Function set global variable by default
    local FromFunc="onetwothree from func" # Set a local variable

}

alias talias='local LocalFromAlias="Local from Alias";  GlobalFromAlias="Global From Alias" # Cant hide a variable with local here '
# Test variables set by tfunc
tfunc # call tfunc
echo $GlobalFromFunc # This is visible
echo $LocalFromFunc # This is not visible
# Test variables set by talias
# call talias
talias
echo $GlobalFromAlias # This is invisible
echo $LocalFromAlias # This variable is unset and unusable 

Output:

bash-3.2$     # Test variables set by tfunc
bash-3.2$     tfunc # call tfunc
bash-3.2$     echo $GlobalFromFunc # This is visible
Global From Func
bash-3.2$     echo $LocalFromFunc # This is not visible

bash-3.2$     # Test variables set by talias
bash-3.2$     # call talias
bash-3.2$     talias
bash: local: can only be used in a function
bash-3.2$     echo $GlobalFromAlias # This is invisible
Global From Alias
bash-3.2$ echo $LocalFromAlias # This variable is unset and unusable

2. wrapper script is a better choice

It has happened to me several times that an alias or function can not be found when logging in via ssh or involving switching usernames or multi-user environment. There are tips and tricks with sourcing dot files, or this interesting one with alias: alias sd='sudo ' lets this subsequent alias alias install='sd apt-get install' work as expect (notice the extra space in sd='sudo '). However, a wrapper script works better than a function or alias in cases like this. The main advantage with a wrapper script is that it is visible/executable for under intended path (i.e. /usr/loca/bin/) where as a function/alias needs to be sourced before it is usable. For example, you put a function in a ~/.bash_profile or ~/.bashrc for bash, but later switch to another shell (i.e. zsh) then the function is not visible anymore. So, when you are in doubt, a wrapper script is always the most reliable and portable solution.

biocyberman
  • 5,675
  • 8
  • 38
  • 50
  • 1
    Why do you think the function version doesn't work? – tripleee Jul 28 '19 at 14:20
  • @tripleee. I've edited the post to make it more readable. Under the heading "1. alias works, function ..." I illustrated why a function version doesn't work. And maybe to answer the question directly, function introduces a new scope where alias doesn't. – biocyberman Aug 05 '19 at 10:00
  • You say it doesn't work but I don't believe you. `source` in a function works just fine. – tripleee Aug 05 '19 at 10:03
  • Does it really work for you? Last time I tried the function quite without giving the expected conda environment. – biocyberman Aug 05 '19 at 10:04
  • 2
    `f () { source /tmp/nst; }` does exactly what I expect. Maybe your `PATH` is wrong so it runs the wrong `activate` or something; but running `source` from a function works fine. – tripleee Aug 05 '19 at 10:06
  • @tripleee You are right. There was an error in my function version that used `source`. I rewrote that part to illustrate the aspect about scope. – biocyberman Aug 05 '19 at 11:41
  • 2
    BTW, re: using the `function` keyword in your definition, see https://wiki.bash-hackers.org/scripting/obsolete -- `function funcname {` is ancient ksh syntax bash supports for backwards compatibility with pre-POSIX shells, whereas `funcname() {` is official POSIX-standardized syntax. `function funcname() {` is a mismash of the two that isn't compatible with ancient ksh **or** compatible with POSIX sh, and which thus is best avoided. – Charles Duffy Feb 12 '20 at 23:34
  • 1
    *"Both functions and aliases can use parameters as others have shown here"* -- the people who claim to have shown that aliases can use parameters (via using `$1`, `"$@"`, etc) misunderstand how their own code works. There's an easy way to demonstrate that they're wrong: `set -- "first argument" "second argument"; alias testAlias='echo "$1"; #'; testAlias "Other String"` ignores `Other String` and writes `first argument`. – Charles Duffy Sep 19 '21 at 17:03
-1
alias junk="delay-arguments mv _ ~/.Trash"

delay-arguments script:

#!/bin/bash

# Example:
# > delay-arguments echo 1 _ 3 4 2
# 1 2 3 4
# > delay-arguments echo "| o n e" _ "| t h r e e" "| f o u r" "| t w o"
# | o n e | t w o | t h r e e | f o u r

RAW_ARGS=("$@")

ARGS=()

ARG_DELAY_MARKER="_"
SKIPPED_ARGS=0
SKIPPED_ARG_NUM=0
RAW_ARGS_COUNT="$#"

for ARG in "$@"; do
  #echo $ARG
  if [[ "$ARG" == "$ARG_DELAY_MARKER" ]]; then
    SKIPPED_ARGS=$((SKIPPED_ARGS+1))
  fi
done

for ((I=0; I<$RAW_ARGS_COUNT-$SKIPPED_ARGS; I++)); do
  ARG="${RAW_ARGS[$I]}"
  if [[ "$ARG" == "$ARG_DELAY_MARKER" ]]; then
    MOVE_SOURCE_ARG_NUM=$(($RAW_ARGS_COUNT-$SKIPPED_ARGS+$SKIPPED_ARG_NUM))
    MOVING_ARG="${RAW_ARGS[$MOVE_SOURCE_ARG_NUM]}"
    if [[ "$MOVING_ARG" == "$ARG_DELAY_MARKER" ]]; then
      echo "Error: Not enough arguments!"
      exit 1;
    fi
    #echo "Moving arg: $MOVING_ARG"
    ARGS+=("$MOVING_ARG")
    SKIPPED_ARG_NUM=$(($SKIPPED_ARG_NUM+1))
  else
    ARGS+=("$ARG")
  fi
done

#for ARG in "${ARGS[@]}"; do
  #echo "ARGN: $ARG"
#done

#echo "RAW_ARGS_COUNT: $RAW_ARGS_COUNT"
#echo "SKIPPED_ARGS: $SKIPPED_ARGS"

#echo "${ARGS[@]}"
QUOTED_ARGS=$(printf ' %q' "${ARGS[@]}")
eval "${QUOTED_ARGS[@]}"

d9k
  • 1,476
  • 3
  • 15
  • 28
  • 2
    This is just a complex and brittle solution to a non-problem. The proper solution is to just not use an alias. (Maybe tangentially notice also how GNU `mv` has a `-t` option which solves this for that particular command in a different way.) – tripleee Jun 01 '22 at 04:57