Last active
November 19, 2022 07:30
-
-
Save XaydBayeck/5648218a2f2d7372693d67f3cfd25901 to your computer and use it in GitHub Desktop.
Run bash file and inherit environment parameters for nushell.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
To be used with a companion fish function like this: | |
function refish | |
set -l _x (python /tmp/bass.py source ~/.nvm/nvim.sh ';' nvm use iojs); source $_x; and rm -f $_x | |
end | |
""" | |
from __future__ import print_function | |
import json | |
import os | |
import signal | |
import subprocess | |
import sys | |
import traceback | |
BASH = 'bash' | |
FISH_READONLY = [ | |
'PWD', 'SHLVL', 'history', 'pipestatus', 'status', 'version', | |
'FISH_VERSION', 'fish_pid', 'hostname', '_', 'fish_private_mode' | |
] | |
IGNORED = [ | |
'PS1', 'XPC_SERVICE_NAME' | |
] | |
def ignored(name): | |
if name == 'PWD': # this is read only, but has special handling | |
return False | |
# ignore other read only variables | |
if name in FISH_READONLY: | |
return True | |
if name in IGNORED or name.startswith("BASH_FUNC"): | |
return True | |
return False | |
def escape(string): | |
# use json.dumps to reliably escape quotes and backslashes | |
return json.dumps(string).replace(r'$', r'\$') | |
def escape_identifier(word): | |
return escape(word.replace('?', '\\?')) | |
def comment(string): | |
return '\n'.join(['# ' + line for line in string.split('\n')]) | |
def gen_script(): | |
# Use the following instead of /usr/bin/env to read environment so we can | |
# deal with multi-line environment variables (and other odd cases). | |
env_reader = "%s -c 'import os,json; print(json.dumps({k:v for k,v in os.environ.items()}))'" % (sys.executable) | |
args = [BASH, '-c', env_reader] | |
output = subprocess.check_output(args, universal_newlines=True) | |
old_env = output.strip() | |
pipe_r, pipe_w = os.pipe() | |
if sys.version_info >= (3, 4): | |
os.set_inheritable(pipe_w, True) | |
command = 'eval $1 && ({}; alias) >&{}'.format( | |
env_reader, | |
pipe_w | |
) | |
args = [BASH, '-c', command, 'bass', ' '.join(sys.argv[1:])] | |
p = subprocess.Popen(args, universal_newlines=True, close_fds=False) | |
os.close(pipe_w) | |
with os.fdopen(pipe_r) as f: | |
new_env = f.readline() | |
alias_str = f.read() | |
if p.wait() != 0: | |
raise subprocess.CalledProcessError( | |
returncode=p.returncode, | |
cmd=' '.join(sys.argv[1:]), | |
output=new_env + alias_str | |
) | |
new_env = new_env.strip() | |
old_env = json.loads(old_env) | |
new_env = json.loads(new_env) | |
script_lines = [] | |
for k, v in new_env.items(): | |
if ignored(k): | |
continue | |
v1 = old_env.get(k) | |
if not v1: | |
# script_lines.append(comment('adding %s=%s' % (k, v))) | |
pass | |
elif v1 != v: | |
# script_lines.append(comment('updating %s=%s -> %s' % (k, v1, v))) | |
# process special variables | |
if k == 'PWD': | |
script_lines.append('cd %s' % escape(v)) | |
continue | |
else: | |
continue | |
if k == 'PATH': | |
# value = '"($env.PATH | prepend [' + ', '.join([escape(directory) | |
# for directory in v.split(':')]) + '])"' | |
value = '['+', '.join([escape(directory) for directory in v.split(':')])+']' | |
else: | |
value = escape(v) | |
script_lines.append('"%s" : %s' % (k, value)) | |
# TODO: really remove repeate env parameters | |
for var in set(old_env.keys()) - set(new_env.keys()): | |
script_lines.append(comment('removing %s' % var)) | |
script_lines.append('hide %s' % var) | |
script = '"env_params" : {\n' + ',\n'.join(script_lines) + "\n}" | |
alias_lines = [] | |
for line in alias_str.splitlines(): | |
_, rest = line.split(None, 1) | |
k, v = rest.split("=", 1) | |
alias_lines.append( '{"nick" : ' + escape_identifier(k) + ' , "aliased" : ' + v.replace("'", '"') + '}') | |
alias = '"alias" : [\n' + ',\n'.join(alias_lines) + "\n]" | |
return "{\n" + script + ',\n' + alias + "\n}" | |
script_file = os.fdopen(1, 'w') | |
if not sys.argv[1:]: | |
# print('__bass_usage', file=script_file, end='') | |
print('__bass_usage', file=os.fdopen(2, 'w'), end='') | |
sys.exit(0) | |
try: | |
script = gen_script() | |
except subprocess.CalledProcessError as e: | |
sys.exit(e.returncode) | |
except Exception: | |
print('Bass internal error!', file=sys.stderr) | |
raise # traceback will output to stderr | |
except KeyboardInterrupt: | |
signal.signal(signal.SIGINT, signal.SIG_DFL) | |
os.kill(os.getpid(), signal.SIGINT) | |
else: | |
script_file.write(script) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export def-env bass [ | |
--debug (-d) | |
...commands:string | |
] { | |
let bash_args = $commands | |
# let script_file = (mktemp) | |
touch bass_temp.nu | |
do -c { | |
# TODO: Add a cheker check if python3 is in PATH | |
python3 -sS ./__bass.py $bash_args | save -r bass_temp.json | |
} | |
if $debug { bat bass_temp.json } | |
let temp = (open bass_temp.json) | |
let new_path = ($env.PATH | prepend $temp.env_params.PATH) | |
let temp = ($temp.env_params | update PATH new_path) | |
load-env $temp.env_params | |
rm bass_temp.json | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment