Red Green Repeat Adventures of a Spec Driven Junkie

How to make oneliners work with sudo

Ever do a single line shell script such as:

vagrant@manager:~$ ls
target_file.txt
vagrant@manager:~$ cat target_file.txt
vagrant@manager:~$ echo "first line" > target_file.txt
vagrant@manager:~$ cat target_file.txt
first line
vagrant@manager:~$ echo "stuff for end of file" >> target_file.txt
vagrant@manager:~$ cat target_file.txt
first line
stuff for end of file

and that is basically, writing “stuff for end of file” to the end of the file named: target_file.txt.

This is really handy because I don’t need to start up an editor to go in an manually write it out. For one line items, it’s perfect.

Chicago Bean

The Real Problem

Well, this technique works well when working with normal access files, special access with sudo is not required.

With system files such as /etc/hosts, which controls local mapping of addresses to names (also known as local DNS entries), this is what happens:

vagrant@manager:~$ echo "192.168.0.100 test.server.internal" >> /etc/hosts
-bash: /etc/hosts: Permission denied

Let’s try with sudo, which I have access:

vagrant@manager:~$ sudo tail /etc/hosts

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost   ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
127.0.1.1       ubuntu-xenial   ubuntu-xenial

vagrant@manager:~$ sudo echo "192.168.0.100 test.server.internal" >> /etc/hosts
-bash: /etc/hosts: Permission denied

So, what gives?! I have access, do I really have to sudo vim here??? Ugh, why did I even bother learning these shell tricks?!

A Solution

There’s another utility in the system called sh, which is an alias to the command interpreter. On my system, sh points to dash

agrant@manager:~$ ls -la /bin/sh
lrwxrwxrwx 1 root root 4 Feb 17  2016 /bin/sh -> dash

There’s another body of knowledge I have to check out. ($ man dash has a slew of information!)

Anyways, to use sh to help solve my oneliner problem, use sh -c, which allows execution of arbitrary programs together. Join this with sudo and we get:

vagrant@manager:~$ sudo echo "192.168.0.100 test.server.internal" >> /etc/hosts
-bash: /etc/hosts: Permission denied
vagrant@manager:~$ sudo sh -c 'echo "192.168.0.100 test.server.internal" >> /etc/hosts'
vagrant@manager:~$ sudo tail /etc/hosts
# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost   ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
127.0.1.1       ubuntu-xenial   ubuntu-xenial

192.168.0.100 test.server.internal

And voilà, my oneline can work with sudo!

One thing to note: for sh -c to accept the oneliner, wrap the oneliner program with matching quotes, single or double. The starting quote style must also be the ending style. So if ' is first, the oneliner must use ", and vice versa.

The above example with quotes swapped:

vagrant@manager:~$ sudo sh -c "echo '192.168.0.101 another.server.internal' >> /etc/hosts"

Conclusion

How to solve oneliner problems when using sudo? Add another command: sh -c to solve it!

Whenever I want to do quick oneliners on system files that require sudo access, I’ll wrap the command up with sudo -c, after I tried the same command on non-system files, of course!