3

I have written a function which acts in a similar way to tee but also pre-pends a datestamp. everything works fine except when i want to output to a file which is only root writable (in my case a logfile within /var/log). I've simplified the following code snippet to just include the bits which are not working:

#!/bin/bash
#script ~/test_logger.sh
logfile=/var/log/test.log
logger()
{
    while read data
    do
        echo $data >> $logfile
    done
    return 0
}
sudo ls ~ | logger

it works fine if i run the whole script like so sudo ~/test_logger.sh but i can't always do this since i want to use the logger function in files like ~/.bash_logout which are run automatically. i've tried putting sudo in front of the echo in the while loop but this does not work. any ideas?

5 Answers 5

8

it's generally bad practice to put sudo in a script. A better choice would be to call the script with sudo from ~/.bash_logout or wherever else you want to use it, if you must, or better still just make /var/log/test.log world-writable.

3
  • yes! i put the whole script into a new file test_logger.sh, made it executeable, then ran it from ~/.bash_logout like so: sudo ~/.bash_logout. works great :) Commented Mar 7, 2011 at 6:55
  • "it's generally bad practice to put sudo in a script." If the script also does operations which do not require root access, then the opposite is true - you would be breaking the principle of least privilege by unnecessarily performing them as root. In this case, elevating only for the parts that require it is completely appropriate. Commented Aug 25, 2019 at 21:57
  • In the OP case, only the part that is writing to the log file needs to run as root. Thus, it could be executed as sudo sh -c 'cat >> /var/log/test.log' or sudo tee -a /var/log/test.log > /dev/null, with the rest of the script running as the current user. Commented Aug 25, 2019 at 21:59
8

as you've found, sudo command >out doesn't work because 'command' is run by sudo, but '>out' is a function of the shell, not 'command'. So, you need to escalate the shell itself:

sudo sh -c "echo $data >>$logfile"

note that you want to be really, really sure what's in $data doing this:

~$ export data='good; touch /tmp/reallybad'
~$ echo $data
good; touch /tmp/reallybad
~$ sudo sh -c "echo $data>>/tmp/happy"
good
~$ ls /tmp/happy /tmp/reallybad
/tmp/happy  /tmp/reallybad

hence simon's warning.

2
  • ah thanks for clarifying the possibilities of [pipe-injection?]. i think the way i ended up doing it (see the comment under simon's solution) is safe since there is no opportunity for anyone to splice extra commands into my ~/.bash_logout file (unless the entire system is already compromised obviously) Commented Mar 7, 2011 at 6:59
  • Remember to quote your variables when passing them to a shell. What if $data happened to contain foo ; rm -rf /? Fix: sudo sh -c "$(printf -- 'echo %q >> %q' "$data" "$logfile")" Commented Aug 25, 2019 at 22:02
3

sudo does not work in the way you might think when you use redirection or pipe operators. The stream change is not executed with sudo permissions. This is why

sudo echo foo >> bar

will not work if bar is only root-writable.

When you run the script under sudo, everything in the script gets superuser permissions so it works correctly in that circumstance.

A workaround is to do this to make sure the writing command is run under sudo:

sudo echo foo | sudo tee bar > /dev/null

Bear in mind, however, that this does not append to the file. It overwrites the file.

3
  • this doesnt work for me - i tried your second command like so sudo echo hi | sudo tee /var/log/test.log > /dev/null but it just continuously prompts for the sudo password. Commented Mar 7, 2011 at 6:01
  • Do you have sudo set-up to prompt for password every time it is used? My configuration allows continuous use of sudo as long as it was successfully used in the last 2~3 minutes so it only prompts for the sudo before the pipe.
    – jamesbtate
    Commented Mar 10, 2011 at 17:41
  • 1
    tee -a appends Commented Aug 28, 2018 at 3:59
1

You are sudoing just the ls command, but not logger. Bash doesn't (and shouldn't) know what sudo does, so it just pipes two commands. The first one is sudo and the second one is logger. No matter if you sudo ls, logger won't.

You should sudo logger, but it won't work because it is a Bash function instead of an executable to be executed by sudo.

BEWARE: ls | sudo logger will invoke /usr/bin/logger if it is installed in your system. Remember: sudo doesn't know about Bash functions.

I'd suggest you adding the user invoking your script to the file owner's group so you're able to write to it without special privilege escalation.

Plus, efficiency sake, you are opening and closing the log file for every line read. You can do it only once for the entire logging process:

function logger() {
  while read data
  do
    echo "$data"
  done
  return 0
} >>"$logfile"
0

Well if you take a look at the man page of sudo you can see examples of how to use it in the scripts... the -c option lets execute a command.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .