3

I'm trying to add a function to my .zshrc that makes adding new USE flags to my /etc/portage/package.use file easier. Normally, I'd have to do

su -c 'echo "net-misc/aria2 bash-completion bittorrent" >> /etc/portage/package.use'

So I tried making a function like

new_use() {
    su -c 'echo "$1" >> /etc/portage/package.use'
}

but then I realized it wouldn't work. I want to know if there's a way of making this function work, or at least if functions are appropriate for this. By the way, I don't mind having to type the root password every time I call the function.

3 Answers 3

7

You can feed stdin to a program with root privileges, like so:

new_use() {
    sudo tee -a /etc/portage/package.use <<< "$1" > /dev/null
}
3
  • Wow, that comes handy. Thanks for the alternate answer! Commented Jan 6, 2013 at 3:08
  • Gotta love unix. I don't think I've ever seen three '<<<' before 8-). Commented Jan 6, 2013 at 3:11
  • @slm, it's a shell feature known as a here string. Commented Jan 6, 2013 at 7:20
3

Just reverse the quotes.

new_use() {
    su -c "echo '$1' >> /etc/portage/package.use"
}

This will cause $1 to be expanded before the execution of su.

4
  • Oh, that's exactly what I wanted. Thank you. Commented Jan 6, 2013 at 3:07
  • 2
    This is probably good enough here, but beware that in general, this is unsafe because it allows the caller of the function to execute arbitrary shell commands by passing an argument that contains a single quote (' >/dev/null; echo evilhacker.example.com >>/root/.rhosts; : '). Commented Jan 6, 2013 at 21:40
  • @Gilles it requires the root password. Commented Jan 7, 2013 at 0:12
  • 2
    @jordanm Which is why it's probably good enough here. But it's not a stretch to extend that to a sudo rule, or to a script that doesn't verify its arguments. Many security holes come from chaining two steps that are safe individually but not in combination. It's better to keep protected at every step. Commented Jan 7, 2013 at 0:15
3

The argument of the function is not passed to the underlying shell. You can substitute the argument inside the string that su will pass to the root shell, but beware that the string will be parsed by the root shell, so special characters in it will be expanded. To avoid this extra expansion (which could cause arbitrary code to be executed as root), interpolate the string between single quotes (which causes all characters other than ' to be interpreted literally), and protect single quotes characters in the string ('\'' terminates the string literal, appends a single quote, and starts a new literal, which means that '\'' is effectively a way to put a single quote inside a literal delimited by single quotes).

new_use() {
  su -c "echo '${1//\'/\'\\\'\'}' >> /etc/portage/package.use"
}

Another possible approach is to use tee.

5
  • Very interesting. So that means you can use single quotes inside single quotes, and your function would be used like new_use 'net-misc/aria2 bash-completion bittorrent' instead of using double quotes, right? Commented Jan 6, 2013 at 22:54
  • I don't understand your question. You can “use single quotes inside single quotes”, but the single quote has to be written '\''. What you write is a valid use of the function, but it could equivalently be written new_use "net-misc/aria2 bash-completion bittorrent" or new_use net-misc/aria2\ bash-completion\ bittorrent or many other ways. Commented Jan 6, 2013 at 22:59
  • Oh, I know you have to escape them if you want to use single quotes inside single quotes. But then I don't think I understand your answer. What makes it different from the others, besides avoiding the extra expansion? Sorry. bash syntax still manages to confuse me. Commented Jan 6, 2013 at 23:43
  • @user1002327 See my comment under jordanm's answer: the problem is when there's a single quote in the argument. Commented Jan 6, 2013 at 23:55
  • Oh, I'm terribly sorry, I hadn't read it before commenting here. Well, the accepted answer is more than enough in this case, but I'll take your advice into account. Didn't know you could do that. Commented Jan 7, 2013 at 0:52

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.