0

I am trying to create a simple alias that uses the argument with the full path

On the command line, I can type command "$(pwd)/my_file". It works. so I tried to create an alias in the following way:

alias command='command "$(pwd)/$1"'

This alias didn't work though. The CLI interprets as if $(pwd) and my_file were separated arguments... I tried to use the eval command to turn my command into a single one

alias command="eval 'command' '$(pwd)/$1'"

However, it keeps waiting for an input argument instead of taking my initial argument...

If you wanna try out what I mean, substitute command for the evince (a popular PDF viewer) and my_file for any PDF file. So my alias is

alias evince="evince $(pwd)/$1"

In my case, $(pwd) is /home/tapyu/Downloads/, and my_file is recap.pdf. I know that evince is treating it as a separated argument because it pops up two windows: The first one opens recap.pdf properly. The second is an empty window with a warning "Error opening file, /home/tapyu/Downloads/ is a directory."

Thank you in advance.

PS: I know that this "problem" is pointless. My problem is not "how to give the full path to a command", my problem is "how to handle inputs argument in an alias in order to solve this kind of situation". So don't wanna alternatives to give the full path, I wanna know why my alias is not working.

  • 4
    What makes you think the shell interprets $(pwd) and my_file as two separate arguments when you define your alias as you show first? How do you actually try to use that alias? Also, aliases do not take arguments. They are simple text replacements. Your second alias makes no sense unless you have set $1 to something at the time of defining the alias (but it's still not what you want, I guess). I believe you may be wanting to use a shell function, but I'm still confused about your talk about "separate arguments". – Kusalananda Aug 09 '21 at 07:44
  • The only way I can think of that would reproduce what you are describing is if you were running this from a directory with a space somewhere in its path. In any case, as Kusalananda said, aliases don't take arguments, you want a function instead, see How to pass parameters to an alias?. In this case, I don't understand why you would even want one, simply run command my_file and that is exactly the same as command $(pwd)/my_file. – terdon Aug 09 '21 at 09:09
  • When you execute an alias defined as alias command="eval 'command' '$(pwd)/$1'", the shell keeps expanding it recursively forever (use set -x to see it). You may use ... eval '\command' ... instead (the backslash prevents alias expansion), but all the points in the previous comments would still stand. – fra-san Aug 09 '21 at 09:17
  • @Kusalananda I know that the shell interprets it as two separate arguments because (for the evince example) it opens two windows: The first one opens the file properly. The second is an empty window with a warning "Error opening file, <pwd-path> is a directory.". – Rubem Pacelli Aug 09 '21 at 15:32
  • @terdon Thank you for the reference. As said, running the command is not the point. I want to know why my alias is not working as I thought and how to fix it. – Rubem Pacelli Aug 09 '21 at 15:34
  • @RubemPacelli in that case, please [edit] your question and show us the actual file name you are using and the output of pwd. I can't reproduce what you describe. The normal behavior is that alias command='command "$(pwd)/my_file"' would work but alias command="eval 'command' '$(pwd)/$1'" would not since $1 wouldn't be defined. So if you are seeing something else, that will be because of the specific paths and/or file names you are using. Most likely you have whitespace somewhere in either the path or the file name. The main point is that aliases cannot take arguments though. – terdon Aug 09 '21 at 15:43
  • With your first alias, running command othername would call command $(pwd)/my_file othername, so the two arguments are $(pwd)/my_file and othername. Is that what you are seeing? I find it very frustrating that you don't say exactly what's happening. It would be easy to tell you to use mycommand () { evince "$PWD/$1"; } if we knew what you wanted to do, but you don't really say what effect you are looking for. – Kusalananda Aug 09 '21 at 15:55
  • @fra-san thank you for the reply. Indeed, using \command solved the problem of recursion. Nevertheless, the command alias command="eval '\command' '$(pwd)/$1'" still treats '$(pwd)/$1' as two separated arguments. – Rubem Pacelli Aug 09 '21 at 16:46
  • @terdon Done. It is exactly what I see on my screen. It doesn't have any space, but I think you find the answer: "The main point is that aliases cannot take arguments though". I didn't know that. Using function would be the sensible way out. – Rubem Pacelli Aug 09 '21 at 16:56
  • Thanks. And which of the two aliases are you using? And what is the alias? Are you setting alias evince='evince...? Or are you using different names? – terdon Aug 09 '21 at 16:58
  • @Kusalananda I'm sorry for the frustration. I made the corrections, now it is correct. but I think terdon find the answer: "The main point is that aliases cannot take arguments though". I didn't know that. Using function would be the sensible way out. – Rubem Pacelli Aug 09 '21 at 16:58
  • @terdon I edited my question once again and put the actual alias. – Rubem Pacelli Aug 09 '21 at 17:02

2 Answers2

2

The first issue is that aliases don't take arguments. If you need to pass an argument to an alias, that means you should use a function instead. You can see what happens if you run set -x:

$ alias evince="evince $(pwd)/$1"
$ set -x
$ evince a.pdf
+ evince /home/terdon/foo/ a.pdf

As you can see, the command evince a.pdf becomes evince /home/terdon/foo/ a.pdf (I was running this in the directory /home/terdon/foo/). So what's going on here? Have a look at what your alias actually is:

$ type evince
evince is aliased to `evince /home/terdon/foo/'

You can even see this happen if you run set -x and then defined the alias:

$ alias evince="evince $(pwd)/$1"
++ pwd
+ alias 'evince=evince /home/terdon/foo/'

When you defined your alias, $1 didn't have any value, and pwd was run before setting the alias, so you actually aliased to evince /home/terdon/foo/. Then, when you ran evince a.pdf, that actually ran evince /home/terdon/foo/ a.pdf which is why you got the two windows.

A function would look like this:

evince(){
    command evince "$(pwd)"/"$1"
}

Note the use of command: that's to ensure that the evince inside the function will call the command evince and not the function itself recursively. Also note that this is a pointless example since evince foo by itself is exactly the same thing as evince $(pwd)foo. You don't need the full path to a file if it is in your current directory.

terdon
  • 242,166
  • What a wonderful explanation. Indeed, I saw what was going on there. Thank you for the precise explanation. Can you edit your answer and add how the function should look like to me accept your answer? – Rubem Pacelli Aug 09 '21 at 17:56
  • @RubemPacelli done and you're welcome. Do note that this is a pointless function though. I assume you actually want to do something different and this is just an example. – terdon Aug 09 '21 at 17:59
  • Yes, this is a pointless example. My initial goal was just how to handle alias with input arguments, now I got it. – Rubem Pacelli Aug 10 '21 at 01:03
1

Aliases don't take arguments, they're just simple text replacement. (Unlike everything else in the shell.) The $1 there would be the first positional parameter of the context where the alias was defined or used. Probably empty in an interactive shell in any case.


What exactly happens also depends on the quotes you use when defining the alias...

If you have

alias evince="evince $(pwd)/$1"

the expansions $(pwd) and $1 will expand right then and there (since they're within double quotes). $(pwd) will expand to the working directory at that point, and $1 to whatever the first positional parameter (command line argument) to the current shell is, probably nothing. So you get something like:

alias evince="evince /some/path/"

After that, running evince foo.pdf runs

evince /some/path/ foo.pdf

indeed with two arguments to evince.


On the other hand, if you have

alias evince='evince $(pwd)/$1'

the expansions do not expand right there, but only when the alias is used. Running evince foo.pdf would expand to

evince $(pwd)/$1 foo.pdf

where $(pwd) is the working directory now, and $1 is still the first positional parameter to the shell, probably empty. Again you get

evince /current/path foo.pdf

(plus any possible issues with the unquoted expansion)

Use a function instead.

See:

ilkkachu
  • 138,973