44

I'm trying to define an alias where the arguments are inserted in the middle, instead of appended to the end.

I tried defining it like this:

alias grep_logs="grep $1 */log/*.log"

where $1 is the first argument to grep_logs, such that:

grep_logs foo

would execute the following command:

grep foo */log/*.log

but instead, it runs the command:

grep foo */log/*.log foo

which results in the error:

grep: foo: No such file or directory

Is it possible to do this using an alias or do I need to define a function?

Jens
  • 69,818
  • 15
  • 125
  • 179
sferik
  • 1,795
  • 2
  • 15
  • 22

6 Answers6

51

Try defining a function in ~/.profile.

function greplogs(){
    grep "$1" */logs/*.log
}
Jack
  • 1,477
  • 15
  • 20
  • 7
    If you want to pass multiple arguments, you can use `${@:1}` instead of `"$1"` and it will insert all arguments there. Might be useful for someone who needs _an alias with arguments in the middle_. – Pijusn Mar 23 '16 at 13:40
  • Just to note, you don't need to use `function`, you can just start with `greplogs() {`. Also, if you do use `function` it will break in ksh. – mVChr Jan 24 '17 at 20:25
  • How to call the function passing the argument? – Eduard Jun 10 '18 at 10:34
  • 1
    @Pijusn Why not `$@`? Although clearly `${@:2}` is useful for skipping the first argument. – piojo Jul 17 '18 at 03:16
  • @piojo you are right they are equal (AFAIK). Not sure why I didn't just suggest `$@`. :) – Pijusn Jul 17 '18 at 07:03
  • `function` is legal ksh syntax, but not with `()` -- you can use `function greplogs {` legally in legacy ksh; by contrast, with POSIX sh, only `greplogs() {` is legal, and `function` is not. – Charles Duffy Oct 18 '21 at 15:14
  • @Eduard, just `greplogs "thing-to-grep-for"`, the same way one passes arguments to any other shell comand. – Charles Duffy Oct 18 '21 at 15:15
23

Just for the sake of answering the question, although the function solution is much cleaner:

alias sstatus='bash -xc '\''sudo service $0 status'\'''
alias sstart='bash -xc '\''sudo service $0 start'\'''
alias sstop='bash -xc '\''sudo service $0 stop'\'''


$sstatus cups
+ sudo service cups status
Status of Common Unix Printing System: cupsd is running.
ealfonso
  • 6,622
  • 5
  • 39
  • 67
16

Expanding upon @erjoalgo's answer which so far is the only one which actually answers the question:

##alias arguments Shell 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 double-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.

Cam
  • 365
  • 3
  • 10
Tom Hale
  • 40,825
  • 36
  • 187
  • 242
4

Not quite the answer you're looking for but use the -e argument if don't want to specify the pattern as the first argument

alias grep_logs="grep */log/*.log -e"
Dunes
  • 37,291
  • 7
  • 81
  • 97
4

The issue is that aliases don't support the concept of positional parameters. If they did, we wouldn't need functions. So yes, use a function because functions are made exactly for this purpose.

Jens
  • 69,818
  • 15
  • 125
  • 179
3

Try using a function and then aliasing it:

function func_grep_logs {
    grep $1 */log/*.log
}

then

alias grep_logs="func_grep_logs"
Femi
  • 64,273
  • 8
  • 118
  • 148