My guess was as follows:

echo "Generating some text" | su - -c cat >/output/file

But su says:

su: must be run from a terminal

What would you do?

  • If you found a solution yourself, please add it as answer below. – Braiam Apr 7 '14 at 3:53
  • @Braiam The solution doesn't deserve to be an answer. – x-yuri Apr 7 '14 at 7:06

sudo supports this.

$ echo hello world | sudo cat  
SUDO password: 
hello world

The difference being that sudo asks for your user password, not the root (target user) password. However if you so desire, you can change this behavior with the targetpw (or runaspw or rootpw) directive in sudoers.conf.

However reading what you're trying to do, while this solves the permission escalation problem, it won't do what you expect. Meaning /output/file will not be created as the root user, it will be created/modified as your user.

The reason for this is that shell output redirection is done before invoking any commands. So the shell opens /output/file and then passes that opened file to su / sudo (and consequently, cat).

However you can use tee to do this instead, as the tee utility will open the file itself.

echo "hello world" | sudo tee /output/file >/dev/null

Basically tee copies the output into /output/file and STDOUT, however STDOUT is redirected into /dev/null.

You could also do:

echo "hello world" | sudo sh -c 'cat > /output/file'

...which is less cryptic.

  • You may also "cache" elevated privileges by invoking sudo -v. It'll ask for your password if you haven't used sudo in a few minutes. – Winny Apr 6 '14 at 20:57

Just so you know - you're not limited to a single command per |pipe:

this happens | then this | { then ; all of ; this too ; } | before this

All of those processes are invoked at the same time - but they all wait on the |pipe before them before actually doing anything - so long as they read the |pipe at all, that is. So, if you need to evaluate a variable midstream, or to setup a redirection, you can. Just take your time.

echo "it takes time" |
{ exec 4>|file ; cat >&4 ; } |
( sleep 1 && cat <file )

it takes time

Here is another way:

echo "more of the same" |
( IFS= ; su -mc 'echo '"$(printf "'%s' " "`cat`")"' >|file' </dev/tty ) |
echo end of pipe

If you don't ( subshell ) the command $(cat) will get </dev/tty as well.

But if you're using a here-doc, you don't need two cats:

rm ./file
su -c 'cat <&3 >|./file; echo "middle here"' 3<<HERE >&2 | {\
    until [ -s ./file ] ; do sleep 1 ; done ;\
    sed 's/beginning/end/' ./file ; }
$(echo "this is the beginning" | sed 'a\of the pipeline' | tee /dev/stderr)


this is the beginning
of the pipeline
middle here
this is the end
of the pipeline

Most of the above is just to demo this. All you really need is:

su -c 'cat <&3 >./file' 3<<HERE | { wait as needed ; more stuff to do ; }
$(echo "something" | and other things)
  • I know that. That has little or nothing to do with the question, or so I think. But somehow it led me to an answer :) See my updated question. – x-yuri Apr 6 '14 at 19:12
  • @x-yuri You can still put a here-document in the middle of a pipeline. echo something | { su 'do some stuff >/record.here' ; } <<HERE | echo end of pipe\n$(cat)\nHERE\n – mikeserv Apr 6 '14 at 20:05
  • Indeed, I didn't think of using cat inside of here document. Anyway, you don't need grouping there. – x-yuri Apr 6 '14 at 20:17
  • @x-yuri - that depends on what you want to do with it. You can use either or both. I was only trying to demonstrate you had options. Patrick's answer is the correct one here, this is just another, more convoluted way. That here-doc does group, though - it groups cat in a subshell. – mikeserv Apr 6 '14 at 20:44
  • By grouping I meant curly braces. – x-yuri Apr 7 '14 at 8:58

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.