Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to export a function, from tmux.conf

I wrote an addon for tmux, tmux-gitbar. It shows some information about Git status of current directory in tmux status bar. The status bar must be updated each time the user runs a command, to do this I added a function, called from the$PROMPT_COMMANDvariable.

What I'm doing for now:

in file .bashrc:

source ~/.tmux-gitbar/tmux-gitbar.sh;

in file tmux-gitbar.sh:

update_gitbar() {
    #
    # update tmux status bar
    #
}
PROMPT_COMMAND="update_gitbar; $PROMPT_COMMAND"


I'd like to achieve the same result without adding a line to .bashrc. All should be performed intmux.conffile. I already managed to modify $PROMPT_COMMAND by doing this in tmux.conf:

PROMPT_COMMAND="update_gitbar; $PROMPT_COMMAND"

Now my problem is to export update_gitbar function into the environment

I tried various ways to source the script, but with no luck:

run-shell "source ~/.tmux-gitbar/tmux-gitbar.sh"
run-shell "eval $(~/.tmux-gitbar/tmux-gitbar.sh)"
run-shell "eval $(cat ~/.tmux-gitbar/tmux-gitbar.sh)"

I always get :

update_gitbar: command not found

Question:

How can I, from tmux.conf file, export a function (declared in another file) and make that function available to the environment?

If that can help, current code is available on github

like image 399
arainone Avatar asked Feb 20 '16 10:02

arainone


3 Answers

Trying to solve it via run-shell is futile. By definition, anything run using run-shell is in a sub-shell/child process, and will not be able to affect tmux's environment.

External shell script

Honestly, I highly recommend that you use an external shell script, instead of a function. Among other things, this will allow you to make changes to update_gitbar that take effect instantly everywhere. It's slightly less efficient, of course, but I don't think you're likely to notice the difference. Just throw it in ~/bin, say, and make PROMPT_COMMAND be "$HOME/bin/update_gitbar; $PROMPT_COMMAND".

However, if you must insist on using shell functions – which I must point out, are a bash-only feature, and was also the source of major security vulnerabilities last year ("ShellShock"; note: not the use of the feature, just its actual existence, and very broken implementation, which has since been fixed in newer versions), there are two solutions I can offer.

Export before running tmux

The first, and simplest way, is just to make sure you do the export in the environment that starts the tmux session, and not after or within it. If you start tmux versus a shell script, say, you'd just do:

export -f update_gitbar
tmux

As part of that startup, you'd also probably want to put the definition of update_gitbar, and your PROMPT_COMMAND setting. Note that once tmux is started, you wouldn't be able to change update_gitbar and have it propagate to everywhere it's being used – that's why I recommended a shell script instead. You would however still be able to modify its meaning for new shells (in new tmux panes, say) using the method described below.

Unportable variable exports

The final method, the hardest and least-portable, is to use tmux's setenv, or the same VAR=value syntax in .tmux.conf. Bash implements its unique "export function" feature just by using a normal export variable, with a special format.

The exact nature of that format is different between different distributions and versions of bash, due to fixing the "ShellShock" security vulnerabilities mentioned above, so (a) what you do in one environment may not work in another, and (b) I can't tell you the exact exported variable name you need to do what you want.

In order to find out what variable format you need, in a regular shell that has update_gitbar, run:

env | sed -n '/^[^=]*update_gitbar/,/^}/p' |
perl -ne 'chomp; $a .= $a? "; $_" : $_; END{$a=~s/=(.*)/='\''$1'\''/; print "$a\n"}'

That complicated command just looks in the environment for the exported shell function and prints its value. Bash includes newlines in the value of the exported variable, so it continues grabbing from the environment until it sees a single line with a closing brace alone. Then it replaces all those newlines with semicolons so that you can put it all on one line. Finally, it replaces the value of the variable inside single quotes, to avoid special meanings of characters. If you have trouble getting a usable value from that command for some reason, you could do that process yourself.

For me, with an example like below illustrates the format on my particular distribution and bash version (notice in the command I changed update_gitbar to hello). As I mentioned, your results may vary, so be sure to try it out on your system.

After you get the variable you want, just plug it into your .tmux.conf, or use the setenv version.

hello() {
    echo hi;
}
export -f hello
hello
>>>> hi
env | sed -n '/^[^=]*hello/,/^}/p' |
perl -ne 'chomp; $a .= $a? "; $_" : $_; END{$a=~s/=(.*)/='\''$1'\''/; print "$a\n"}'
>>>> BASH_FUNC_hello%%='() {  echo hi\!; }'

(The >>>>-prefixed lines are output, not part of the script in the example.)

like image 75
Micah Cowan Avatar answered Nov 19 '22 00:11

Micah Cowan


tmux's set-environment looks promising and might be of help to you. Let me quote the relevant section from man tmux:

set-environment [-gru] [-t target-session] name [value]
              (alias: setenv)
        Set or unset an environment variable.
        If -g is used, the change is made in the global environment; 
        otherwise, it is applied to the session environment for target-session.
        The -u flag unsets a variable.
        -r indicates the variable is to be removed from the environment before starting a new process.

You might want to use set-environment -g PROMPT_COMMAND update_gitbar as an instruction inside your tmux-gitbar.conf which will be sourced from .tmux.conf via source /path/to/tmux-gitbar.conf

EDIT: Forgot to mention that update_gitbar should be an exectuable file somewhere in PATH.

P.S.

Cool plugin! :-)

like image 44
Rany Albeg Wein Avatar answered Nov 19 '22 00:11

Rany Albeg Wein


Whereas I have to point out that there are already viable answers, I want to mention one more solution to your problem.

There's a mod adding full-fledged scripting to tmux: http://ershov.github.io/tmux/ (I'm the author).

It not only allows to create new commands:

proc update_gitbar {} { run-shell "source ~/.tmux-gitbar/tmux-gitbar.sh" }

(for your case)

... but also allows you to bind several actions to 'mode' keystrokes.

It's also usable for writing complete plugins in that language without a need to spawn a shell at all.

like image 1
Yuriy Ershov Avatar answered Nov 19 '22 01:11

Yuriy Ershov