14

I have customised .bashrc with a number of alias, specifically ll and export LS_OPTIONS='--color=auto'

Unfortunately this does not work when used with sudo, so I also modified /root/.bashrc, but this seems to have made no difference.

sudo env shows HOME=/root and SHELL=/bin/bash

How can I get sudo commands to use the settings in /root/.bashrc?

I understand that this happens only when bash is executed interactively, so I am open to any other suggestions as to how to customise.

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Milliways
  • 1,378
  • @daniel-gelling - I've always felt like this Q is a XY problem - https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem. The title implies they want anything out of /root/.bashrc but really what the Q is after is the aliases from this file - this is not possible per this - https://unix.stackexchange.com/questions/1496/why-doesnt-my-bash-script-recognize-aliases. – slm Jul 26 '18 at 14:01
  • @slm What i'm after is adding a method to bashrc - as I've done in my personal .bashrc file as well as the .bashrc file for root to "disable" the -r option for crontab: crontab () { [[ $@ =~ -[iel]*r ]] && echo '"r" not allowed' || command crontab "$@" ;}. This works when logged in as either user, however when I execute sudo crontab -r it still executes. – Daniel Gelling Jul 26 '18 at 14:32
  • @DanielGelling - see if my answer works for you scenario. – slm Jul 26 '18 at 14:34

5 Answers5

6

sudo runs an executable, not a shell command. So it doesn't know about aliases. If you run sudo ls, that's like sudo /bin/ls, it doesn't use any ls alias that you may have.

You can cause sudo ls to expand the alias by putting the following in your .bashrc:

alias sudo='sudo '

Note the trailing space — that tells the shell to continue alias expansion with the word that comes after sudo. Beware that expanding aliases after sudo may not always be a good idea, it depends what kinds of aliases you have.

Furthermore sudo removes most variables from the environment. This won't affect an alias like alias ls='ls $LS_OPTIONS', because that's a shell variable used by the shell while it's expanding the command (and exporting it from .bashrc serves no purpose). But it would affect variables that are used by the command, such as LS_COLORS. You can configure sudo to keep certain environment variables by editing its configuration: run visudo and add the line

Defaults env_keep += "LS_COLORS"

With these settings, sudo ll will give the colors you're used to.

Alternatively, you can run a root shell with sudo -s. This shell will load its configuration file (~/.bashrc for bash). Depending on how sudo is configured, this may keep HOME set to your home directory, or change it to /root. You can force the home directory to be set to root's with sudo -Hs; conversely, to keep the original home directory, run sudo env HOME="$HOME" bash.

5

Thanks to those who answered, who prompted me to read man sudo more carefully.

sudo -s If no command is specified, an interactive shell is executed.

This interactive shell uses /root/.bashrc and thus includes my customisations.

It does require the command be entered separately, but this is OK.

Milliways
  • 1,378
4

Background

I've always felt like this question is an XY problem. The title implies they want anything out of /root/.bashrc but really what the question is after is the aliases from this file - this is widely believed to not be possible per this - Why doesn't my Bash script recognize aliases?.

It's basically by design that your aliases will not get picked up in sudo and in other places because they're not portable, and this is my opinion on them as well.

Anything that's in the user's environment should not be assumed by scripts and any software that may run on a given box. But I realize that there are scenarios where there might be some aliases in a given user account's $HOME/.bashrc that others might want to leverage in interactive scenarios.

To that end you can simply tell the Bash interpreter to expand any aliases that it finds during the login process outside of the normal shell behaviors that you run into when using sudo.

Example

Setup

To set things up I've added the following aliases, environment variables, and functions to my root user's /root/.bashrc and /root/.bash_profile files.

$ grep smurf ~/.bashrc
alias brc_smurf='echo "ran alias from /root/.bashrc"'
export brc_smurf_env='var from /root/.bashrc'
bpf_smurf_func() { echo 'ran func from /root/.bash_profile'; }

$ grep smurf ~/.bash_profile
alias bpf_smurf='echo "ran alias from /root/.bash_profile"'
export bpf_smurf_env='var from /root/.bash_profile'
brc_smurf_func() { echo 'ran func from /root/.bashrc'; }

Without doing anything neither of these work (no surprises):

$ sudo brc_smurf
sudo: brc_smurf: command not found

$ sudo bpf_smurf
sudo: bpf_smurf: command not found

We see that the alias command shows no aliases when run in sudo:

$ sudo alias
$

This behavior is your hint that you shouldn't expect aliases to be accessible. But we carry on...

Step #1 - aliases visible

If we run bash -ci we can induce Bash to at least read our $HOME/.bashrc:

$ sudo bash -ci 'alias' | grep smurf
alias brc_smurf='echo "ran alias from /root/.bashrc"'

Cool, so maybe we can run it?

$ sudo bash -ci 'alias; brc_smurf'
bash: alias; brc_smurf: No such file or directory

Step #2 - shopt -s expand_aliases

Nope. Again this is by design, we're doing something we're not suppose to be doing, so there's a number of "safeties" that we have to disable. the other "safety" is Bash.

$ sudo bash -ci 'shopt -s expand_aliases; alias; brc_smurf'
alias brc_smurf='echo "ran alias from /root/.bashrc"'
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
ran alias from /root/.bashrc

Here we can see our message from /root/.bashrc, we've successfully executed the root user's alias brc_smurf.

Step #3 - what about env vars?

If you're using the method shown above these should now work as well.

$ sudo bash -ci 'shopt -s expand_aliases; brc_smurf; echo $brc_smurf_env'
ran alias from /root/.bashrc
var from /root/.bashrc

Step #4 - what about functions?

These works too as expected:

$ sudo bash -ci 'shopt -s expand_aliases; brc_smurf; echo $brc_smurf_env;brc_smurf_func'
ran alias from /root/.bashrc
var from /root/.bashrc
ran func from /root/.bashrc

TLDR;

You can do this to get access to environment variables + aliases from /root/.bashrc:

$ sudo bash -ci 'shopt -s expand_aliases; <cmds>'

Take aways

This method enables the contents of /root/.bashrc, it does not pick up the contents of /root/.bash_profile.

References

slm
  • 369,824
  • Yep, even though there are some minor differences between them, let's not go into detail here ;-). Anyways, could you help me with the situation I stated in the comments of the question: using functions in .bashrc with sudo; specifically removing the -r option from crontab? – Daniel Gelling Jul 26 '18 at 14:58
  • Okay, but this would mean that I'd have to run: sudo bash -ci 'alias; shopt -s expand_aliases; echo $brc_smurf_env' instead of a simple sudo echo $brc_smurf_env? – Daniel Gelling Jul 26 '18 at 15:02
  • You can remove the alias, that was simply to show them, you'd need to do sudo bash -ci 'shopt -s expand_aliases; <cmds>' – slm Jul 26 '18 at 15:07
  • That would be a lot of typing just to edit my crontab for root. Let me create an alias for it :-P – Daniel Gelling Jul 26 '18 at 15:09
  • @DanielGelling - yup, welcome to all the fun down in the shell bowels. – slm Jul 26 '18 at 15:10
  • @DanielGelling - the other approach is shell functions, those are exportable so you might want to reformulate your plan of attack. – slm Jul 26 '18 at 15:11
  • Worth noting that it's always a major naughtiness to to rely on external aliases when writing scripts. Aliases are intended to assist usability for humans. The key problem here is that it breaks portability of scripts. Having said that, functions are fine, and if you really, really must use aliases you define them inline within your script, with good explanatory comments. – Brian C Dec 09 '20 at 01:06
0

There are a bunch of settings in the /etc/sudoers file specifically or setting up an environment for when sudo commands are run (eg. Making sure the PATH only has trusted locations) but depending on what you're hoping to get out of this you might not be able to do what you're after if it involves running actual commands in a shell to set up the environment. Sudoing specifically doesn't give you a root login shell so it won't set up a regular profile for you.

hvindin
  • 111
0

TL;DR: You can use sudo -i to run a function defined /root/.bashrc (but not an alias) and also have access to variables exported from that file:

sudo -i command arguments

Aliases don't work there, though, but you can easily convert them to functions if you'd like to make them available to sudo -i.

Read on for full analysis and more details.


There are a few problems here, some in how sudo works and some in how bash itself works...

By default, sudo will only look for commands and will bypass the shell, so simply running sudo ll will only work if there's an ll executable in one of the directories in $PATH. So, in order to use aliases (or functions) you need to make sure a shell is invoked as part of the process.

One way would be to run something like sudo sh or sudo bash, though modern sudo (I'm testing this on sudo 1.8.19p1) has options -s and -i for that purpose.

So one try would be something like sudo -s ll (which is equivalent to sudo bash -c 'll', assuming your $SHELL is Bash, which seems to be the case based on the rcfile you mentioned.) But that doesn't work either, since it starts the shell in a non-interactive, non-login mode, which doesn't read any of its startup files. It's essentially the same as if you write a shell script and use #!/bin/bash to run it. The aliases (and functions) you have in your ~/.bashrc won't be accessible from that script...

So next is the -i option, which creates a login shell. That's more promising, since it will read your startup files! And yet, sudo -i ll (equivalent to sudo bash -l -c 'll') will still not work. So how is that possible, given it did read the definition of the ll alias?

Well, the next explanation here is that, by default, bash will not expand aliases, except when the shell is interactive... This shell started by sudo -i (or bash -l) is a login shell, but still not interactive.

So the next step is to get an interactive shell, which then works:

sudo bash -i -c 'll'

(Having both login and interactive is also fine, of course, bash -l -i -c ... will work.)

Another alternative is to keep using a login shell (non-interactive) but ask it explicitly to expand aliases, so this will also work:

sudo bash -l -O expand_aliases -c 'll'

(The case where bash was interactive didn't need a login shell, since that is enough to read the initialization files, but this one needs -l to read them.)

These are fairly long command lines... And they also require that you quote the whole shell command, so if you're calling an alias with arguments, you'll have to turn all that into a string... So it's kind of clumsy to use...

Note that earlier I was talking about aliases and functions... That was on purpose, since functions are actually a lot more convenient here. You don't need anything special (such as having an interactive shell, or setting a specific option) to execute functions on a shell, as long as you're sourcing their definition.

So if you defined ll as a function instead of an alias, you'd be able to use it directly with sudo's -i shortcut:

sudo -i ll

And if you have a longer command-line, with arguments, you could pass them directly here too:

sudo -i ll -C -R /etc

(Compare to sudo bash -i -c 'll -C -R /etc'.)

Functions are also a lot more flexible and typically easier to maintain... It's usually easy to convert an alias into a function, the only caveat is to always use "$@" where you'd expect extra arguments to be taken (typically at the end of the alias.)

For example, this alias:

alias ll='ls $LS_OPTIONS -l'

Could be turned into this function:

ll () {
    ls $LS_OPTIONS -l "$@"
}

They are, for most purposes, equivalent. And, as mentioned before, the function should be accessible directly from sudo -i, so that's an added bonus.

I hope you find this answer and explanation helpful!

filbranden
  • 21,751
  • 4
  • 63
  • 86
  • DV - I don't feel like this is improving the situation anymore than what's already here either. – slm Jul 26 '18 at 13:29
  • 1
    @slm I do think my answer is adding something, since no previous answer mentioned the form sudo -i command arguments to be able to run functions from /root/.bashrc and have exported variables available. But I see how my answer was perhaps too long and that info was somewhat buried in there... So I added a TL;DR to summarize it (while still keeping the technical details of the investigation around.) Please take another look. – filbranden Jul 26 '18 at 15:38