Ubuntu – How to easily make an alias command permanent?

aliasbashbashrccommand line

I search everywhere and almost everyone was suggesting to open the ~/.bashrc or ~/.bash_aliases, then put the alias command there, I created a function name palias as permanent alias function and put it in ~/.bashrc:

function palias(){ echo "alias $1='$2'" >> /home/User/.bashrc;}

So now if I want to permanently alias some command I just :

palias update "sudo apt-get update"

I wonder if there is a built in command like this function, or anything like this to install from repositories?

Best Answer

  • There is no bash builtin to add permanent aliases. Such aliases are created by adding them to a configuration file that is sourced by interactive shells, as you've done with .bashrc.

    But you might consider modifying your palias() implementation by making it put the aliases in a separate file. I think this may be considered an improvement, for six reasons:

    1. The file that is mostly edited automatically can be separate from the file that is mostly edited manually. If you need to see what has happened--out of curiosity, or to investigate a problem--this separation may be helpful. (Or if the function is accidentally called many times, creating a large number of unnecessary lines, the damage will be somewhat more contained.)

    2. By putting the individual, automatically generated alias definitions elsewhere than .bashrc, you create a system that "works behind the scenes," which is often what people want when they look for a dedicated builtin to perform a task.

    3. .bashrc is already usually quite complicated. It will be easier to read and modify for unrelated purposes if it's not filled with many user-defined automatically appended aliases.

    4. Simple implementations of palias() -- including what you've written and what I'm about to recommend -- will create bad lines if the file they're appending to is not already newline-terminated (or blank). Normally text editors do this for you but it's possible to accidentally end a text file without a newline, in which case palias() will create a munged-together line that may or may not be correct, and which either way would be very difficult to read. By putting aliases in a separate file that you rarely, or at least less frequently, edit manually, you decrease the risk of this.

    5. If you ever expand your palias() implementation to read the existing contents of its target file, for example, to give useful warnings, it may be much easier to do so if can be assumed to consist entirely of alias definitions. (Or, for more sophisticated parsing: entirely of alias definitions and lines assumed to be wrong.)

    6. It is already widely suggested (though admittedly not as any sort of strong admonition) to put new aliases in a separate file: .bash_aliases

      See How do I create a permanent Bash alias? for more information about places to put aliases.

    Default .bashrc files in Ubuntu contain the code:

    # Alias definitions.
    # You may want to put all your additions into a separate file like
    # ~/.bash_aliases, instead of adding them here directly.
    # See /usr/share/doc/bash-doc/examples in the bash-doc package.
    
    if [ -f ~/.bash_aliases ]; then
        . ~/.bash_aliases
    fi
    

    Thus if you create .bash_aliases, it will be used. The command to define the palias() function can then be:

    palias() { echo "alias $1='$2'" >> ~/.bash_aliases; }
    

    That command should still go in .bashrc, but the aliases it creates will be placed in .bash_aliases.

    More sophisticated implementations are possible. You could:

    1. Run the definition as well as adding it, so it's immediately usable. This is suggested in osirisgothra's answer. I'd use:

      palias() { echo "alias $1='$2'" >> ~/.bash_aliases; alias "$1=$2"; }
      
    2. Check to ensure .bash_aliases is nonexistent, empty, or newline-terminated, and if it is nonempty but not newline-terminated, append a newline prior to the alias definition.

      palias() {
          local target=~/.bash_aliases
          [[ -f "$target" && -n $(tail -c1 "$target") ]] && echo >> "$target"
          echo "alias $1='$2'" >> "$target"
      }
      

      See Gilles's answer to Add missing newlines in multiple files for details on detecting missing newlines with tail.

    3. Check and warn about defining an alias that has already been defined in .bash_aliases.

      Ploutox suggested that idea. But I do not recommend using grep $1 ~/.bash_aliases or similar, because it will produce frequent false positives, anytime $1 matches any part of another alias's definition, and occasional false negatives. The matter is further complicated by how some valid bash aliases, taken as regular expressions, match more than themselves--+ and . actually get used in command and alias names--and a few don't match even themselves.
      grep "alias $1=" is better but mitigates the problem only partially. A leading ^ in the pattern would pretty well match only the leading alias name of a definition, but not address the problem of regexp metacharacters in $1, while using grep -F would strip all metacharacters of their special meaning but in doing so prevent ^ from being used.

      greping .bashrc in this way is even worse than .bash_aliases, as there's an essentially unlimited variety of text that may be part of that file, mostly outside any alias definitions.

      If you want to check for aliases that are defined in the current shell, whether or not they're written in .bash_aliases (or anywhere), you can use alias "$1" (or run alias with no arguments and match, carefully, against its output). Credit goes to muru for this idea. It does have the disadvantage that if you define an alias with alias to test it out and then make it permanent with palias, you'll get a warning even though it was not yet permanent and everything is fine.

      To check new permanent aliases against the contents of .bash_aliases, you could do something like this:

      palias() {
          local target=~/.bash_aliases
      
          if grep -Pqs '^[ \t]*alias[ \t]+\Q'"$1"'\E\=' "$target"; then
              printf "%s: %s: warning: defining \`%s' again in %s\n" \
                          "$0" "$FUNCNAME" "$1" "$target" >&2
          fi
      
          echo "alias $1='$2'" >> "$target"
      }
      

      That seems to work well, though I suspect there are more elegant ways. (Arguably [ \t]* should be removed, if you take leading whitespace before alias as a suggestion that it is part of a multiline alias definition.) It will malfunction if \E appears in $1, but shell metacharacters and quoting characters like \ aren't valid in alias names, so you won't need to give that as input.
      Ideally, on any wrong input, any user-facing function you call would perform no operation except printing a useful error message. But by the nature of palias(), at least unless you're prepared to make its definition far more sophisticated (see below), the burden of reasonable input already falls mostly on the user. You can write non-aliases and alias definition syntax errors to .bash_aliases with this function, and while that's probably acceptable given the way you'll be using palias(), choking on \E is minor in comparison.

      More seriously, alias name1='def1' name2='def2' ... is perfectly good syntax for the alias command, but I haven't tried to parse out alias definitions given as subsequent arguments to alias in .bash_aliases, as I believe correctly determining where the first non-quoted blank occurs would be non-trivial.

      Therefore, if you use the above palias() implementation and also sometimes manually define aliases in .bash_aliases (or whatever file you use as $target), you should define just one alias per alias ... line if you want to have reliable warning behavior.
      It will also fail to recognize alias definitions to the right of a ; or other connectives, for example to chain separate alias commands on the same line, but it seems unlikely you'd want to do that in .bash_aliases.

    4. Parse $1 to ensure it is a valid name for an alias.

    5. Parse $2 to attempt to figure out if it is a plausible body for an alias, and if it is not, then warn, fail, or (where applicable) quote it properly. For example, what happens if a ' character appears in $2?

    6. Do a simpler version of that instead, just checking for and addressing ' in $2.

    But you likely don't want to bother with any of that. You know how your function works, its limitations, and how to use it properly. I see four options for you that are at the same time reasonably good and pretty simple:

    1. Just keep using what you have now.
    2. Use an implementation as above, putting the aliases in .bash_aliases instead of .bashrc.
    3. Make a third file for automatically added aliases, and source that file separately (either in .bashrc or .bash_alises.
    4. Don't use a palias() function at all, and just manually add aliases by editing .bash_aliases or .bashrc.