Repository: xonsh/xonsh

Issue:
Take over bash aliases?
Custom bash aliases (eg. in ~/.bash_aliases) do not work after starting xonsh from bash.

Maybe documenting how to best port them to xonsh-aliases ist enough.



Discussion:
Not sure about the automatic porting (xonsh doesn't have to be started from bash after all), but `alias ll='ls -l'` in bash is equivalent to `aliases['ll'] = ['ls', '-l']` in xonsh, which is [documented in the tutorial](http://xonsh.org/tutorial.html#aliases). You can put the respective lines in a ~/.xonshrc file to execute them automatically when xonsh starts. Do you have any suggestions how to make this clearer in the tutorial maybe?

I understand that automatic porting might not be a good idea. What about an alias (similar to source-bash) to a function that can swallow a .bash_aliases as argument and parse it to xonsh-aliases?

For the documentation, maybe mention potential pit-falls when naively parsing/converting bash-aliases? Being new to xonsh, I was reluctant to just run a loop over the lines and split the right-hand-side.

Anyway, thank for xonsh! :)

Hi all, this is a feature that I do want to have, I just haven't gotten around to it yet.  I would be happy if someone else did :). Here is how I would do it.  In `xonsh.environ.bash_env()` or a similar function I would add the bash built-in command `alias` to the input (ie `env && alias`).  The alias command in bash prints all your current aliases like follows:

``` console
scopatz@athenaie ~ BA$H alias
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias git-commit-count='git log --pretty=format:'\'''\'' | wc -l'
alias git-fat='nice git-fat'
alias grep='grep --color=auto'
alias ipynb='ipython notebook --no-browser'
alias ipynb2='ipython2 notebook --no-browser'
alias ipynb3='ipython3 notebook --no-browser'
alias ls='ls --color=auto -v'
alias mocp='mocp -T transparent-background'
alias nano='xo'
alias pep8='pep8 --ignore=E501 -r'
alias scp-resume='rsync --partial -h --progress --rsh=ssh'
alias screenshot='import -window root'
```

From here you can parse out the name and alias string easily.  The alias string should then be passed through the `shlex.split()` function in the Python standard library before being set in the xonsh aliases.

You should not try to parse the `bash_aliases` file directly for a couple reason. (1) who knows what is written in there (crazy conditional aliases etc), (2) aliases can be defined outside of this file too (ie bashrc).  It is much better to let bash figure all that stuff out.

The reason for doing this in the `bash_env()` function is that it would need to start up in `bash -i` mode, and I have found that time can be noticeable. Better to do this once during xonsh startup than multiple times. 

There is also a question, though: will `shlex.split` and subprocess handle things like pipes, ${...}, etc?  My initial reaction is that it will be hard to make something that robustly handles all cases, but I could be missing something.

shlex would correctly handle parsing things like `${}` and it would parse the pipe correctly.  The current `subproc_run()` probably would not correctly deal with a pipe in an alias though.  Of course if a bash alias was to a bash function, that would be pretty broken here too.  

I don't think the point here is to have 100% bash-compatibily.  That would involve making a lot of concessions in xonsh that would decrease usability.  Rather, I think that the goal should be to incorporate the parts of bash that are reasonable and useful.  This way we provide a transition out of bash into something better.

I think that's absolutely right, but it does mean that if xonsh moves toward something that tries to automatically grab aliases from bash, it should fail gracefully on those aliases that can't be converted.

Yup! I totally agree, @wrywerytwreywery 


Resolving patch:
diff --git a/xonsh/aliases.py b/xonsh/aliases.py
--- a/xonsh/aliases.py
+++ b/xonsh/aliases.py
@@ -4,6 +4,8 @@
 import platform
 import builtins
 import subprocess
+import shlex
+from warnings import warn
 
 def cd(args, stdin=None):
     """Changes the directory.
@@ -55,6 +57,27 @@ def source_bash(args, stdin=None):
         env[k] = v
     return
 
+def bash_aliases():
+    try:
+        s = subprocess.check_output(['bash', '-i'], input='alias',
+                                    stderr=subprocess.PIPE,
+                                    universal_newlines=True)
+    except subprocess.CalledProcessError:
+        s = ''
+    items = [line.split('=', 1) for line in s.splitlines() if '=' in line]
+    aliases = dict()
+    for key,value in items:
+        try:
+            key = key[6:]
+            value = value.strip('\'')
+            value = shlex.split(value)
+            aliases[key] = value
+        except Exception as exc:
+            warn('could not parse Bash alias "{0}": {1!r}'.format(key,exc),
+                    RuntimeWarning)
+    return aliases
+
+
 DEFAULT_ALIASES = {
     'cd': cd,
     'EOF': exit,
diff --git a/xonsh/built_ins.py b/xonsh/built_ins.py
--- a/xonsh/built_ins.py
+++ b/xonsh/built_ins.py
@@ -16,7 +16,7 @@
 from xonsh.tools import string_types, redirect_stdout, redirect_stderr, suggest_commands
 from xonsh.inspectors import Inspector
 from xonsh.environ import default_env
-from xonsh.aliases import DEFAULT_ALIASES
+from xonsh.aliases import DEFAULT_ALIASES, bash_aliases
 
 ENV = None
 BUILTINS_LOADED = False
@@ -415,6 +415,7 @@ def load_builtins(execer=None):
     builtins.execx = None if execer is None else execer.exec
     builtins.compilex = None if execer is None else execer.compile
     builtins.aliases = Aliases(DEFAULT_ALIASES)
+    builtins.aliases.update(bash_aliases())
     BUILTINS_LOADED = True
 
 def unload_builtins():


Test patch: