You likely shouldn't do that. You can probably achieve your goal in way that does not involve writing one script that generates another script that you will later modify and run; see below. But if you really want to do it, and you want to do it with code that closely resembles what you've shown, then steeldriver is right that a here document is a workable way.
The way steeldriver uses a here document is fine; here's another, whose code is a bit simpler. (I'm presenting it mainly for the opportunity to explain the meaning of each part of its syntax, not out of the belief that this approach is superior.)
#!/bin/bash
cat >/usr/local/sbin/adduser.local <<'EOF'
#!/bin/bash
lastuser="$(grep home /etc/passwd | cut -d: -f 1 | tail -1)"
EOF
Replace /usr/local/sbin/adduser.local
with whatever name you want for the second script, if it is different. Then simply run the script as root, for example with sudo
. The entire point of this script is to create a file in a location only root has write access to, so it is itself the sort of command that makes sense to run as root with sudo
. When you have a script in which every command starts with sudo
(especially if it's a one-command script), that often suggests that the script shouldn't use sudo
inside of it at all but should instead just be run with sudo
.
Depending on how you're running the generated script, you may need to give it execute permissions, which you can do with chmod +x
. (That applies to steeldriver's way, as well.) If the file already exists and has execute permissions, and you're overwriting it, then you don't need to run chmod
.
The way the command shown above works is that:
- When
cat
is run with no filename arguments (as is the case here), it simply copies its input to its output.
cat
's output is redirected to the file /usr/local/sbin/adduser.local
by the output redirection operator >
. I've used the >
operator, which overwrites, even though you were using >>
, which appends. I'm doing this because I assume you want to overwrite that file, because the text you're placing in it starts with a hashbang, which doesn't usually make sense except at the beginning of a file.
cat
's input is taken from the text that appears on the following line, up to but not not including any of the line that begins with EOF
, stipulated by the here document notation <<
.
- Quoting
EOF
after <<
-- that is, writing <<'EOF'
rather than <<EOF
-- suppresses expansions in the body of the here document, so that none of the characters in the bash code there are treated specially by the shell running the first script.
Using a here document eliminates difficulties associated with quotes that are nested inside other quotes. If you ever need the body of a here document to contain EOF
, that's fine, because although EOF
is one of the popular choices of word, it's not special at all. You can use whatever word you like. Just use it both after <<
and at the beginning of a line that appears by itself after the last line you wish to provide as input--just as EOF
is used above.
There are several reasons you should probably not actually do that (nor use any echo
-based method) in the situation you appear to be describing.
First, if your goal is to allow non-root users to install updates, editing sudoers
might not be the best way to achieve that. There are other approaches; for example, you can schedule updates for automatic unattended installation or let non-root users use the update manager and you may be interested in this technique or some variation on it. I'm not sure quite what your requirements are, but you might want to ask about whatever specific problem you're trying to solve.
Second, if you do end up editing /etc/sudoers
, it's usually bad to do so in a way that does not involve automatically checking for syntax errors and rejecting modifications that introduce them. (It is also often better to create or edit a sudoers file in the /etc/sudoers.d
directory rather than modifying the main sudoers file /etc/sudoers
, but the same cautions and considerations apply.) When you manually edit a sudoers file with the visudo
command, such checks are performed; the file you're editing is not a real sudoers file, but is instead a temporary file that is checked and only copied over to a real sudoers file after passing syntax checks.
The slightest syntax error in any sudoers file causes sudo to refuse to allow anyone to perform actions as root! This is often easy to fix, but if the reason you're not using a Polkit-based approach is that this is a server system without Polkit, then it's a bit harder. If you must edit a sudoers file not through visudo
, you can take the precautionary measure of opening a root shell, separate from the shell in which you're running the commands that modify your configuration, such as by running sudo -i
. Once started, this will remain usable, for as long as it is running, even if sudo
is broken.
You can actually use visudo
to check the syntactic correctness of automated, scripted modifications to sudoers files! This is possible because it accepts any command as the text editor to use to edit the file; this can be customized for a single run of visudo
using the SUDO_EDITOR
environment variable, and you can write a script that behaves as that editor but is automated and requires no interactions (except perhaps in the event that it produces a syntax error that visudo
blocks). I'd consider the details beyond the scope of this answer; if you want help doing something like that, you could ask another question about it.
Third, it looks like you're planning to add several (perhaps many) entries to a sudoers file, for individual users. If so, you should consider adding a single entry for a group and adding the users to the group. This is the approach used to allow administrators on an Ubuntu system to run arbitrary commands as root with sudo
; such users are members of the sudo
group, which /etc/sudoers
has an entry for. You can create other groups and give them other permissions, which can be more restrictive than the ability the sudo
group has to run arbitrary commands.
Fourth, even if you do want to follow the approach you're taking, there are better ways than having one script embed a fragment that it uses to seed other scripts that you manually modify. The two approaches I suspect you might find most useful are:
- You can put
lastuser="$(grep home /etc/passwd | cut -d: -f 1 | tail -1)"
in a file, which need not be executable nor start with a #!
hashbang line, and then source that file in whatever scripts are going to use lastuser
, by having those scripts contain the command . lastuser.sh
(or source lastuser.sh
, which is equivalent in bash), where lastuser.sh
is the name of the file that contains the command. The lastuser
variable gets defined at the time the . lastuser.sh
command runs, so it will still always depend on the current state of /etc/passwd
.
- If you really do want one script to create another, then rather than embedding the code that will be copied into the first script, you can make a master file that contains it have the first script just copy that file to the desired location. You could do the copying with
cp
or install
.