--- lshell/shellcmd.py | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) --- a/lshell/shellcmd.py +++ b/lshell/shellcmd.py @@ -26,6 +26,7 @@ import re import signal import readline import glob +import subprocess from utils import get_aliases @@ -128,6 +129,9 @@ class ShellCmd(cmd.Cmd, object): # builtin export function elif self.g_cmd == 'export': self.export() + # builtin source function + elif self.g_cmd == 'source': + self.source() # case 'cd' is in an alias e.g. {'toto':'cd /var/tmp'} elif self.g_line[0:2] == 'cd': self.g_cmd = self.g_line.split()[0] @@ -214,6 +218,14 @@ class ShellCmd(cmd.Cmd, object): os.environ.update({var: value.rstrip()}) + def source(self): + """ implementation of the "source" command + """ + # ensure if command contains at least 1 space + if self.g_line.count(' '): + source_script = self.g_line + self.sourceShell(source_script) + def cd(self): """ implementation of the "cd" command """ @@ -515,6 +527,57 @@ class ShellCmd(cmd.Cmd, object): else: self.prompt = '%s:%s$ ' % (self.promptbase, path) + def sourceShell(self, script): + """Source the shell script and call env when done in order to \ + detect the new environment and then use that to update the \ + environ of the lshell process. + """ + pipe = subprocess.Popen("%s; env -0" % script, + bufsize=1, + stdout=subprocess.PIPE, + shell=True) + + iterator = iter(pipe.stdout.readline, b'') + outputlist = list(iterator) + output = '' + for i, line in enumerate(outputlist): + if i == (len(outputlist) -1): + output = line + else: + sys.stdout.write(line) + + # output may pick up some echos at the end of script and merge + # with the first line in env. Test for this and echo those to stdout + envList = output.split('\0') + firstenv = re.findall('^\S+=\S+$', envList[0], re.MULTILINE) + if firstenv: + print envList[0].strip(firstenv[0]) + envList[0] = firstenv[0] + newenv = {} + for line in envList: + env = line.split("=", 1) + if len(env) is not 2: + continue + newenv.update(dict([env])) + os.environ.update(newenv) + + def loginCmdParse(self, script): + """Parse the login command specified in login_script. \ + If login_script or a sub script sources a bash config \ + then call shell_source() + """ + # if multiple commands are chained together, execute + # them individually. We will not support conditional + # chaining (&& or ||) since that would required the + # additional complexity of checking the retcode of + # the previous command + cmds = script.split(";") + for cmd in cmds: + if "source" in cmd: + self.sourceShell(cmd) + else: + os.system(cmd) + def cmdloop(self, intro=None): """Repeatedly issue a prompt, accept input, parse an initial prefix \ off the received input, and dispatch to action methods, passing them \ @@ -541,7 +604,7 @@ class ShellCmd(cmd.Cmd, object): if self.intro and isinstance(self.intro, str): self.stdout.write("%s\n" % self.intro) if self.conf['login_script']: - os.system(self.conf['login_script']) + self.loginCmdParse(self.conf['login_script']) stop = None while not stop: if self.cmdqueue: