Controlling KDE activities via dbus
KDE can be controlled from the command line with qdbus
. The general syntax is qdbus COMPONENT PATH METHOD ARGUMENT1...
where COMPONENT
is typically something like org.freedesktop.Foo
or org.kde.Bar
, PATH
denotes a class exposed by the component, METHOD is the name of a particular action in that class, and there may be further arguments depending on the method.
Here are commands for KDE ≥4.7 to list activities, to get the current activity, and to set the current activity.
qdbus org.kde.kactivitymanagerd /ActivityManager org.kde.ActivityManager.ListActivities
qdbus org.kde.kactivitymanagerd /ActivityManager org.kde.ActivityManager.CurrentActivity
qdbus org.kde.kactivitymanagerd /ActivityManager org.kde.ActivityManager.SetCurrentActivity "activity identifier "
Finding out what dbus can do
KDE's dbus documentation is very poor. Each class is minimally documented, e.g. Activity, DesktopCorona). But you'll probably have to experiment and perhaps read the source (there are links in the API documentation pages) to find out what is available.
If you type qdbus
with up to two arguments, it will list the possibilities for the next argument. The following shell snippet lists all available Qt-dbus methods:
for x in $(qdbus | sed '/^:/d'); do
for y in $(qdbus $x); do
qdbus $x $y | sed "s~^~$x $y ~"
done
done 2>/dev/null >qdbus.list
Another way to explore the dbus tree is qdbusviewer
in the Qt development tools. There is also a Python qt-dbus interface as part of PyQt.
Getting the shell to react
To make a shell react to external events, the best you can reasonably do is make it check something before displaying a prompt. Bash runs $PROMPT_COMMAND
before displaying a prompt, and zsh executes the precmd
function. So you can look up the current KDE activity and do something if it's changed from the last time you looked.
There is unfortunately no fully portable location to set environment variables. The two files that come closest are ~/.profile
, which is the traditional location and works out of the box on many setups, and ~/.pam_environment
, a modern, commonplace but limited alternative.
What to put in ~/.pam_environment
The file ~/.pam_environment
is read by all login methods that use PAM and that have this file enabled. This covers most Linux systems nowadays.
The major advantage of ~/.pam_environment
is that (when enabled) it is read before the user's shell starts, so it works regardless of the session type, login shell and other complexities. It even works for non-interactive logins such as su -c somecommand
and ssh somecommand
.
The major limitation of ~/.pam_environment
is that you can only put simple assignments there, not complex shell syntax. The syntax of this file is as follows.
- Files are parsed line by line.
- Each line must have the form
VAR=VALUE
where VAR consists of letters, digits and underscores. The alternative form VAR DEFAULT=value
allows expansions of environment variables using ${VAR}
syntax and the special variables @{HOME}
and @{SHELL}
.
#
starts a comment, it cannot appear in a value.
- If VALUE is surrounded by
"
, then VAR is set to the string between the quotes.
\$
or \@
insert a literal $
or @
and long lines can be split by escaping the newline with a \
.
- If there is a syntax error such as no
=
or unquoted whitespace, the variable is removed from the environment.
So on the upside, ~/.pam_environment
works in a large array of circumstances. On the downside, you cannot use the output of a command (e.g. test if a directory or program is present), and some characters (#"
, newline) are impossible or troublesome to put in the value.
What to put in ~/.profile
This file should have portable (POSIX) sh syntax. Only use ksh or bash extensions (arrays, [[ … ]]
, etc.) if you know that your system has these shells as /bin/sh
.
This file may be read by scripts in automated applications, so it should not call programs that produce any output or call exec
. If you want to do that on text-mode logins, do it only for interactive shells. Example:
case $- in *i*)
# Display a message if I have new mail
if mail -e; then echo 'You have new mail'; fi
# If zsh is available, and this looks like a text-mode login, run zsh
case "`ps $PPID` " in
*" login "*)
if type zsh >/dev/null 2>/dev/null; then exec zsh; fi;;
esac
esac
This is an example of using /bin/sh
as your login shell and switching to your favorite shell. See also how can I use bash as my login shell when my sysadmin refuses to let me change it
When is ~/.profile
not read on non-graphical login?
Different login shells read different files.
If your login shell is bash
Bash reads ~/.bash_login
or ~/.bash_profile
if they exist instead of ~/.profile
. Also bash does not read ~/.bashrc
in a login shell even if it is interactive. To never have to remember these quirks again, create a ~/.bash_profile
with the following two lines:
. ~/.profile
case $- in *i*) . ~/.bashrc;; esac
See also Which setup files should be used for setting up environment variables with bash?
If your login shell is zsh
Zsh reads ~/.zprofile
and ~/.zlogin
, but not ~/.profile
. Zsh has a different syntax from sh, but can read ~/.profile
in sh emulation mode. You can use this for your ~/.zprofile
:
emulate sh -c '. ~/.profile'
See also Zsh not hitting ~/.profile
If your login shell is some other shell
There's not much you can do there, short of using /bin/sh
as your login shell and your favorite shell (such as fish) as an interactive shell only. That's what I do with zsh. See above for an example of invoking another shell from ~/.profile
.
Remote commands
When invoking a remote command without going through an interactive shell, not all shells read a startup file.
Ksh reads the file specified by the ENV
variable, if you manage to pass it.
Bash reads ~/.bashrc
if it is not interactive (!) and its parent process is called rshd
or sshd
. So you can start your ~/.bashrc
with
if [[ $- != *i* ]]; then
. ~/.profile
return
fi
Zsh always reads ~/.zshenv
when it starts. Use with caution, since this is read by every single instance of zsh, even when it is a subshell where you've set other variables. If zsh is your login shell and you want to use it to set variables only for remote commands, use a guard: set some variable in ~/.profile
, such as MY_ENVIRONMENT_HAS_BEEN_SET=yes
, and check this guard before reading ~/.profile
.
if [[ -z $MY_ENVIRONMENT_HAS_BEEN_SET ]]; then emulate sh -c '~/.profile'; fi
The case of graphical logins
Many distributions, display managers and desktop environments arrange to run ~/.profile
, either by explicitly sourcing it from the startup scripts or by running a login shell.
Unfortunately, there is no general method to handle distro/DM/DE combinations where ~/.profile
is not read.
If you use a traditional session started by ~/.xsession
, this is the place where you should set your environment variables; do it by sourcing ~/.profile
(i.e. . ~/.profile
). Note that in some setups, the desktop environment startup scripts will source ~/.profile
again.
Best Answer
There're mature tools designed to set environment variables for a specific directory.
Compared with other tools designed for this,
direnv
is the best of them. One of the main benefit is that it supports unloading the environment variables when you exit from that directory.What makes
direnv
distinct between other similar tools:direnv
is written in Go, faster compared with its counterpart written in Pythondirenv
supports unloading environment variables when you quit from the specific dirdirenv
covers many shellsSimilar projects