I'm working with a PyQt application that requires the user to source environment variables from a Bash file. I can see these variables in the terminal after sourcing the file, but when I try to access them inside my Qt application using
os.getenv(), they returnNone. How can I access sourced Bash environment variables from within a PyQt or PySide application?
When you open a terminal and run source setup.sh, the variables defined in that script are loaded into that specific shell session's environment. Any child process launched from that same shell session will inherit those variables.
However, your Python/Qt application is a separate process. If it was launched from a different shell session — for example, from an IDE, a desktop shortcut, or a different terminal window — it won't have access to variables sourced in another session. Each process gets its own copy of the environment at the time it starts, and changes in one process don't propagate to others.
There's also a subtle timing issue that can cause confusion. Consider this code:
out = subprocess.run([
"bash", "-c",
f"source /path/to/setup.sh -r && "
f"{os.getenv('MY_VAR')}"
], stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
Here, os.getenv('MY_VAR') is evaluated by Python before the subprocess starts. At that point, the variable hasn't been sourced yet, so it resolves to None. The subprocess ends up trying to run bash -c "source /path/to/setup.sh -r && None", which produces the error bash: None: command not found.
The fix is to keep everything inside the Bash command string so that Bash evaluates the variable after sourcing:
out = subprocess.run([
"bash", "-c",
"source /path/to/setup.sh -r && echo $MY_VAR"
], stderr=subprocess.STDOUT, stdout=subprocess.PIPE, text=True)
print(out.stdout.strip()) # Should print the value of MY_VAR
By using $MY_VAR inside the Bash command string (not an f-string interpolation of os.getenv()), Bash itself resolves the variable after the source command has run.
Approach 1: Read variables via subprocess
If you only need to read a few specific variables, you can source the file in a subprocess and echo the values back to Python:
import subprocess
def get_env_var_from_bash(script_path, var_name):
"""Source a Bash script and retrieve a single environment variable."""
result = subprocess.run(
["bash", "-c", f"source {script_path} && echo ${var_name}"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
value = result.stdout.strip()
return value if value else None
# Usage
ocpi_root = get_env_var_from_bash(
"/path/to/cdk/opencpi-setup.sh -r",
"OCPI_ROOT_DIR",
)
print(ocpi_root)
This works well for grabbing one or two values. If you need many variables, though, spawning a subprocess for each one gets inefficient.
Approach 2: Load all sourced variables into your Python process
A more thorough approach is to source the Bash file in a subprocess, dump the entire environment, and then parse it back into Python's os.environ. This makes every sourced variable available throughout your application — including inside your PyQt or PySide widgets.
import os
import subprocess
def load_bash_env(script_path):
"""Source a Bash script and load all resulting env vars into this process."""
command = f"source {script_path} && env"
result = subprocess.run(
["bash", "-c", command],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
if result.returncode != 0:
print(f"Error sourcing script: {result.stderr}")
return
for line in result.stdout.splitlines():
# Environment variables are in KEY=VALUE format.
# Split on the first '=' only, since values can contain '='.
if "=" in line:
key, _, value = line.partition("=")
os.environ[key] = value
# Source the file early, before creating your Qt application
load_bash_env("/path/to/cdk/opencpi-setup.sh -r")
# Now these work anywhere in your code
print(os.getenv("OCPI_ROOT_DIR"))
Once you've called load_bash_env(), every part of your application — including Qt widgets, signals, slots, and helper modules — can use os.getenv() or os.environ to access those variables normally.
One thing to be aware of: some environment variables may contain newlines in their values (this is rare but possible). The simple line-by-line parsing above handles the vast majority of cases, but if your setup script produces multi-line values, you may need to use the null-byte-delimited approach shown below:
import os
import subprocess
def load_bash_env(script_path):
"""Source a Bash script and load all resulting env vars into this process.
Uses null-byte delimited output to handle values containing newlines.
"""
command = f"source {script_path} && python3 -c \"import os; print('\\0'.join(k+'='+v for k,v in os.environ.items()))\""
result = subprocess.run(
["bash", "-c", command],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
if result.returncode != 0:
print(f"Error sourcing script: {result.stderr}")
return
for entry in result.stdout.split("\0"):
if "=" in entry:
key, _, value = entry.partition("=")
os.environ[key] = value
Approach 3: Use the envbash package
The envbash package wraps up the logic of sourcing a Bash file and importing the environment variables into your Python process. It handles edge cases for you and provides a clean API.
Install it with pip:
pip install envbash
Then use it like this:
import os
from envbash import load_envbash
# Source the script and load all variables into os.environ
load_envbash("/path/to/cdk/opencpi-setup.sh -r")
# Now accessible everywhere
print(os.getenv("OCPI_ROOT_DIR"))
This is the most concise option, and it works well if you're comfortable adding an external dependency.
Putting it all together with PyQt6
Here's a complete example showing how to source a Bash environment file and use the resulting variables inside a PyQt6 application. This uses the manual subprocess approach (Approach 2) so there are no extra dependencies.
import os
import subprocess
import sys
from PyQt6.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QVBoxLayout,
QWidget,
)
def load_bash_env(script_path):
"""Source a Bash script and load all resulting env vars into this process."""
command = f"source {script_path} && env"
result = subprocess.run(
["bash", "-c", command],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
if result.returncode != 0:
print(f"Error sourcing script: {result.stderr}")
return
for line in result.stdout.splitlines():
if "=" in line:
key, _, value = line.partition("=")
os.environ[key] = value
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Environment Variable Viewer")
layout = QVBoxLayout()
# Read some environment variables and display them
home = os.getenv("HOME", "Not set")
path = os.getenv("PATH", "Not set")
ocpi_root = os.getenv("OCPI_ROOT_DIR", "Not set")
layout.addWidget(QLabel(f"HOME: {home}"))
layout.addWidget(QLabel(f"PATH: {path[:80]}..."))
layout.addWidget(QLabel(f"OCPI_ROOT_DIR: {ocpi_root}"))
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def main():
# Load environment from a Bash script before creating the app.
# Replace this path with your actual script path.
script_path = os.path.expanduser("~/cdk/opencpi-setup.sh")
if os.path.exists(script_path):
load_bash_env(script_path)
else:
print(f"Script not found: {script_path} (skipping)")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
The pattern is straightforward: call load_bash_env() early in your program (before you create any widgets that need the variables), and then use os.getenv() anywhere in your application just as you normally would.
Which approach should you use?
| Approach | Best for |
|---|---|
| Subprocess per variable | Quick lookups of one or two values |
| Load all via subprocess | Full access to sourced variables, no extra dependencies |
envbash package |
Clean API, handles edge cases, okay with adding a dependency |
All three approaches solve the same underlying problem: getting variables from a Bash environment into your Python process so that your Qt application can see them. Pick whichever fits your project best, and you'll have those variables available wherever you need them.
Create GUI Applications with Python & Qt6 by Martin Fitzpatrick
(PySide6 Edition) The hands-on guide to making apps with Python — Over 15,000 copies sold!