754

This is a pretty simple question, at least it seems like it should be, about sudo permissions in Linux.

There are a lot of times when I just want to append something to /etc/hosts or a similar file but end up not being able to because both > and >> are not allowed, even with root.

Is there someway to make this work without having to su or sudo su into root?

0

15 Answers 15

1158

Use tee --append or tee -a.

echo 'deb blah ... blah' | sudo tee -a /etc/apt/sources.list

Make sure to avoid quotes inside quotes.

To avoid printing data back to the console, redirect the output to /dev/null.

echo 'deb blah ... blah' | sudo tee -a /etc/apt/sources.list > /dev/null

Remember about the (-a/--append) flag! Just tee works like > and will overwrite your file. tee -a works like >> and will write at the end of the file.

Sign up to request clarification or add additional context in comments.

6 Comments

I absolutely prefer this one. It's just the simplest (and it tought me about tee, which comes in handy in other scenarios as well).
I agree. Seems neater than start a new sh too, especially with potentially to do things with environment etc.
One important thing to note: NEVER forget the -a! Just imagine what a echo 'tmpfs /tmp tmpfs defaults 0 0' | sudo tee /etc/fstab would do
Under OS X, this should be tee -a instead of tee --append.
For those who don't understand what @mic_e said: without the -a (--append) flag the command would overwrite the whole file with the given string instead of appending it to the end.
|
377

The problem is that the shell does output redirection, not sudo or echo, so this is being done as your regular user.

Try the following code snippet:

sudo sh -c "echo 'something' >> /etc/privilegedfile"

7 Comments

What are "sudo permission boundaries"? It's just the shell which parses the redirection operator with higher precedence than a command for obvious reasons
Depending on your sh, echo can interpret escape sequences like \t inside single quotes. You could use printf %s 'something' instead.
Using tee is more popular and more compatible to newer distros.
This is the only one which works as-is as an SSH command which can be executed on a remote node.
I agree with other posts here. This uses a shell and only shell so another program like tee is not required here. Yes I know tee is installed already, but maybe not depending on what micro distro you're using like bare bones alpine. I like that you have a shell option too: sudo /bin/bash -c "echo 'fs.inotify.max_user_watches = 524288' > /etc/sysctl.d/60-golang-inotify.conf" for example.
|
37

The issue is that it's your shell that handles redirection; it's trying to open the file with your permissions not those of the process you're running under sudo.

Use something like this, perhaps:

sudo sh -c "echo 'something' >> /etc/privilegedFile"

3 Comments

@GordonSun this is because sudo (for security reasons) doesn't propagate the environment to the subprocess. You may use sudo -E to bypass this restriction.
@GordonSun yes it will, I don't understand why you keep repeating this statement! If you do sudo sh -c "echo 'something' >> $FILENAME", with double quotes, this will work - the variable substitution is done by the outer shell, not the sudoed shell.
does not work with multiline echo
24
sudo sh -c "echo 127.0.0.1 localhost >> /etc/hosts"

Comments

16

Doing

sudo sh -c "echo >> somefile"

should work. The problem is that > and >> are handled by your shell, not by the "sudoed" command, so the permissions are your ones, not the ones of the user you are "sudoing" into.

Comments

11

I would note, for the curious, that you can also quote a heredoc (for large blocks):

sudo bash -c "cat <<EOIPFW >> /etc/ipfw.conf
<?xml version=\"1.0\" encoding=\"UTF-8\"?>

<plist version=\"1.0\">
  <dict>
    <key>Label</key>
    <string>com.company.ipfw</string>
    <key>Program</key>
    <string>/sbin/ipfw</string>
    <key>ProgramArguments</key>
    <array>
      <string>/sbin/ipfw</string>
      <string>-q</string>
      <string>/etc/ipfw.conf</string>
    </array>
    <key>RunAtLoad</key>
    <true></true>
  </dict>
</plist>
EOIPFW"

2 Comments

This works great, except the embedded quotes might need to be escaped with a \
Quite right @NJones! I shall edit my answer. (Note, however, that not doing this strips the internal " but does not cause the command to fail.)
11

Some user not know solution when using multiples lines.

sudo tee -a  /path/file/to/create_with_text > /dev/null <<EOT 
line 1
line 2
line 3
EOT

Comments

10

In bash you can use tee in combination with > /dev/null to keep stdout clean.

 echo "# comment" |  sudo tee -a /etc/hosts > /dev/null

Comments

5

Using Yoo's answer, put this in your ~/.bashrc:

sudoe() {
    [[ "$#" -ne 2 ]] && echo "Usage: sudoe <text> <file>" && return 1
    echo "$1" | sudo tee --append "$2" > /dev/null
}

Now you can run sudoe 'deb blah # blah' /etc/apt/sources.list


Edit:

A more complete version which allows you to pipe input in or redirect from a file and includes a -a switch to turn off appending (which is on by default):

sudoe() {
  if ([[ "$1" == "-a" ]] || [[ "$1" == "--no-append" ]]); then
    shift &>/dev/null || local failed=1
  else
    local append="--append"
  fi

  while [[ $failed -ne 1 ]]; do
    if [[ -t 0 ]]; then
      text="$1"; shift &>/dev/null || break
    else
      text="$(cat <&0)"
    fi

    [[ -z "$1" ]] && break
    echo "$text" | sudo tee $append "$1" >/dev/null; return $?
  done

  echo "Usage: $0 [-a|--no-append] [text] <file>"; return 1
}

Comments

3

You can also use sponge from the moreutils package and not need to redirect the output (i.e., no tee noise to hide):

echo 'Add this line' | sudo sponge -a privfile

1 Comment

-a appends to file.
2

By using sed -i with $ a , you can append text, containing both variables and special characters, after the last line.

For example, adding $NEW_HOST with $NEW_IP to /etc/hosts:

sudo sed -i "\$ a $NEW_IP\t\t$NEW_HOST.domain.local\t$NEW_HOST" /etc/hosts

sed options explained:

  • -i for in-place
  • $ for last line
  • a for append

1 Comment

I think you've been downvoted because only GNU sed lets you put text immediately after the command. On OSX I had to actually use line breaks AND the -e option: sudo sed -ie '$a\ <newline> > export GOOGLE_APPLICATION_CREDENTIALS="adapter-gcp-compute-test-WWWXXXYYYZZZ.json" <newline> > ' /Users/gcpclient/.bashrc
0

echo 'Hello World' | (sudo tee -a /etc/apt/sources.list)

Comments

-1

How about:
echo text | sudo dd status=none of=privilegedfile
I want to change /proc/sys/net/ipv4/tcp_rmem.
I did:
sudo dd status=none of=/proc/sys/net/ipv4/tcp_rmem <<<"4096 131072 1024000"
eliminates the echo with a single line document

1 Comment

Please take a moment to read through the editing help in the help center. Formatting on Stack Overflow is different than other sites.
-2

This worked for me: original command

echo "export CATALINA_HOME="/opt/tomcat9"" >> /etc/environment

Working command

echo "export CATALINA_HOME="/opt/tomcat9"" |sudo tee /etc/environment

1 Comment

Should be tee -a. Just tee will overwrite the file!
-7

Can you change the ownership of the file then change it back after using cat >> to append?

sudo chown youruser /etc/hosts  
sudo cat /downloaded/hostsadditions >> /etc/hosts  
sudo chown root /etc/hosts  

Something like this work for you?

1 Comment

Chown is a destructive command to use. If a config manager used that to update /etc/hosts, suddenly /etc/hosts belongs to config user and not root. which potentially leads to other processes not having access to /etc/hosts. The whole point of sudo is to avoid having something logged into root and via sudoers more restrictions can be piled on as well.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.