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?

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.

  • 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 :) – mulllhausen Mar 7 '11 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. – Vladimir Panteleev Aug 25 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. – Vladimir Panteleev Aug 25 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.

  • 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) – mulllhausen Mar 7 '11 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")" – Vladimir Panteleev Aug 25 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.

  • 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. – mulllhausen Mar 7 '11 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 Mar 10 '11 at 17:41
  • 1
    tee -a appends – dave_thompson_085 Aug 28 '18 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.