41

I have an alias in my .bashrc that calls some other script, passing it specific command-line arguments.

I want to write a python script that would call this alias every few minutes so I can have a shell open with updated stats.

However, subprocess.call("myAlias"), tried like so, fails:

from subprocess import call

def callAlias():
    call("myAlias")

callAlias()

How can I make Python use the alias from my .bashrc?

2
  • 2
    In a case like this, it is probably better to put the script name and all the arguments in a list of strings and use that for your call, rather than rely on an alias that may or may not be present. Commented Aug 21, 2012 at 21:41
  • Avoiding the use of an alias is the easiest and common solution to this problem. Either inline the alias into your call() args, or convert the alias into a script that call() can invoke. Commented Oct 11, 2018 at 17:25

4 Answers 4

95

Update: Thanks for the upvotes for this hack to workaround the problem, I'm glad it's useful. But a much better answer is tripleee's.


If the alias you require is defined in ~/.bashrc, then it won't get run for a few reasons:

  1. You must give the shell keyword arg:

    subprocess.call('command', shell=True)
    

    Otherwise your given command is used to find an executable file, rather than passed to a shell, and it is the shell which expands things like aliases and functions.

  2. By default, subprocess.call and friends use the /bin/sh shell. If this is a Bash alias you want to invoke, you'll need to tell subprocess to use bash instead of sh, using the executable keyword arg:

    subprocess.call('command', shell=True, executable='/bin/bash')
    
  3. However, /bin/bash will not source ~/.bashrc unless started as an 'interactive' shell (with -i.) Unfortunately, you can't pass executable='/bin/bash -i', as it thinks the whole value is the name of an executable. So if your alias is defined in the user's normal interactive startup, e.g. in .bashrc, then you'll have to invoke the command using this alternative form:

    subprocess.call(['/bin/bash', '-i', '-c', command])
    # i.e. shell=False (the default)
    
Sign up to request clarification or add additional context in comments.

5 Comments

good answer. it's now recommended (and easier) to use subprocess.run(), rather than subprocess.call()
How do you translate the above command for subprocess.run. I tried subprocess.run(['echo', "something"], capture_output=True, shell=True, check=True, env=my_env)
I know you decided to put yourself down, and thanks for linking to the informative answer of triplee. But you did answer the question as posed, and even if the 'better' answer is 'don't use an alias', I still found your answer very useful as a learning exercise. Many thanks!
Cool ! This thing worked for me when invoking 'nvm' from inside subprocess method in Python script.
@BND, docs are NOT incredibly obvious, but you have to pass the command all in one string. In your case, it's subprocess.run(['/bin/bash', '-i', '-c', 'echo something']).
26

You need to set the shell keyword to True:

call("myAlias", shell=True)

From the relevant documentation:

If shell is True, the specified command will be executed through the shell. This can be useful if you are using Python primarily for the enhanced control flow it offers over most system shells and still want access to other shell features such as filename wildcards, shell pipes and environment variable expansion.

Aliases are a shell feature (e.g. they are defined and interpreted by the shell).

However, the shell (/bin/sh) is executed non-interactively, so no .profile or .bashrc files are read and your alias probably is not going to be available.

If you are reluctant to use the full expanded command into your python script, you'll have to use the $ENV environment variable to make the shell read the file with the alias defined in it anyway:

call("myAlias", shell=True, env=dict(ENV='/path/to/aliasfile'))

3 Comments

Thanks! This has be once step close, except now I'm getting the error /bin/sh: myAlias: command not found. Running the alias exactly as it is in my script works, so it's not a sourcing issue...
Aliases are only enabled in interactive shells by default.
FWIW, bash aliases aren't as powerful as bash shell functions.
25

The recommended solution is to not use an alias to define functionality which isn't exclusively for interactive use (and even then, shell functions are superior in a number of ways).

Refactor the alias into a standalone script and call it like any other external command.

In more detail, if you have

alias myalias='for x in foo bar baz; do
    frobnicate "$x"; done'

you can improve it so it doesn't pollute your global namespace by turning it into a function

myalias () {
    local x
    for x in foo bar baz; do
        frobnicate "$x"
    done
}

or just save it as /usr/local/bin/myalias and chmod a+x /usr/local/bin/myalias to make it executable for everyone;

#!/bin/sh
for x in foo bar baz; do
    frobnicate "$x"
done

(This runs in a subprocess so x will be gone when the script finishes; so we don't need to make it local.)

(Of course, if frobnicate is at all competently written, maybe you can simplify to just frobnicate foo bar baz as well.)

This is a common FAQ.

6 Comments

You have been robbed on the voting for this answer. Yours should be the top/accepted answer, instead of my ghastly convoluted hack.
@JonathanHartley Thanks for the upvote and the comment (-:
@rubmz That doesn't sound at all like you need an alias. In fact, it sounds like yet another situation where an alias would be inferior to a function, shell wrapper, or PATH adjustment.
say you want to programatically start an existing app. That app can be installed anywhere in the os, but it has (and will always have) an alias, a short catchy alias which you want to invoke. Won't that be useful?
If there is a situation where only an alias provides this, perhaps you could link us to a fuller exposition of why that is.
|
6

I modified Jonathan's reason #2 slightly to make this work. Make a /usr/local/bin/interactive_bash file that contains

#!/bin/bash
/bin/bash -i "$@"

and chmod +x it. Then from Python you can call

subprocess.call('my_alias', shell=True, executable='/usr/local/bin/interactive_bash')

2 Comments

Interesting idea, especially for when hacking around on your own machine. Might be tricky if deploying to someone else's machine though.
Yeah, this probably isn't something I'd feel good about using in production :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.