What does "read -p" do in a linux shell script? [closed] - linux

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 7 years ago.
Improve this question
I have a script that I have copied and edited. There are a couple of lines in there that I need explaining if possible please.
These are the lines:
read -p "please enter the username you wish to create: " username
if id -u $username >/dev/null 2>&1; then
What does read -p do? What does id -u do? What does >/dev/null 2&1; do?
Then further on in the script, it has this line that says this:
sudo useradd -g $group -s $bash -d $homedir -m $username -p $password
Again please could someone explain all the minus signs in this line please? (-g, -s, -d, -m, -p)

First off, the structure <command> -<option> means that you want to execute <command> using the option corresponding to <option>. A - after a command means that the following letter is an option. Most commands have several options you can use. Options are usually defined using either a single letter or a couple of words separated by -.
Side Note: For options that are a couple of words rather than a single letter, often it will use two minus signs -- instead of one, signifying that it is a "long named" option.
So, using the read -p example, this means you want to execute read using the p option, which stands for prompt.
Now, sometimes an option will require an argument. In your examples, the options to useradd have arguments. Arguments are usually defined like <command> -<option> [argument]. So, in the useradd example, $group is an argument for the option g.
Now for the commands themselves:
read is a bash built-in (not a POSIX shell command) that reads from standard input.
The -p option makes it read as a prompt, meaning it doesn't add a trailing newline before trying to read input.
if checks the return status of the test command (in this case id -u $username >/dev/null 2>&1)
If the return status is 0, the then part is executed
id prints user groups and ids
The -u option "prints only the effective user ID".
>/dev/null 2>&1 redirects standard input and standard error to /dev/null, meaning they do not get printed to the terminal.
useradd creates a new user
-g sets the initial group for the user
-s sets the name of the user's login shell
-d sets the name of the user's login directory
-m says to create the user's home directory if it does not exist.
-p defines the user's encrypted password.
For future reference, you can look up commands in the linux manual pages by doing man <command> on the command line. These manual pages tell you what a command does, as well as explaining all of its options.
Bash built-ins like read are all on a single man page that is not the easiest thing to use. For those I find googling them easier. Usually http://ss64.com/ will come up in the results, which contains the info from the bash built-ins man page, but separated into different pages by command. I find this much easier to use.

Related

How to redirect to a root file as non-root with sudo [duplicate]

This question already has answers here:
How do I use sudo to redirect output to a location I don't have permission to write to? [closed]
(15 answers)
Closed 1 year ago.
How to do that with a regular sudoers user:
wget -q https://example.com/binary.tar.gz -O - | tar -xzO binary > /usr/local/bin/binary # Doesn't work because I can't write in /usr/local/bin as regular user
I can't use sudo with the redirect >. So what is the appropriate way to do it?
Subsidiary question: I know I can use tee for the following case:
echo "foo" > /etc/myapp.conf # Doesn't work
echo "foo" | sudo tee -a /etc/myapp.conf # Solution
But I know this is not the goal of tee. So the question is the same for this case: what's the best solution?
EDIT:
I don't want to use a subshell with something like sudo sh -c 'my-command'. One reason is to limit as much as possible the number of commands launched as root.
This is a question to find a solution in the "linux standard", not some kind of hacking.
I think the goal of tee is to write output to some files AND in the stdout. I don't want to use something which can do that, but something which exists to do that.
The reason you cannot redirect to a file you don't have write access to, is because the shell does the opening of the file. When using sudo only the process that are ran with special privileges, consider the following example:
$ sudo my-cmd --flag1 --flag2 > file
The shell invokes the command sudo with the arguments: my-cmd, --flag1 and --flag2, and tries to open a file called file, and connect the sudo process' standard output to the input of the file.
The command sudo will elevate the users permissions and execute the command my-cmd with the arguments --flag1 and --flag2.
Understanding this it also becomes clear why you cannot just prefix with sudo to write to a file that you don't have permissions to.
Instead you can, as you have already discovered, use tee as it can take a path as an argument and open it for writing.
Alternative you can invoke another shell with special privileges:
$ sudo sh -c 'my-cmd --flag1 --flag2 > file'
This will invoke the shell sh with escalated privileges. This however becomes quite tedious as you'll need to stick your whole command into one argument for sh. This will cause quotes and glob to be hard to write properly.

Bash: redirect input from 2 sources? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I want to execute a Linux command in Bash with the input coming from a file or echo, and then switch back to standard input as if the command's input was not redirected.
So basically, I want to feed the first part from an interactive command with a predefined text, and as soon as that text is "consumed" I want to continue using the keyboard (stdin).
Some examples:
Prefill editor and then continue typing manually
(echo blablabla; cat) | nano
Auto remove first file, then manually confirm removing second file
touch dummyfile1.txt; touch dummyfile2.txt
(echo y; cat) | rm -i dummyfile*.txt
Fill in password for dummyuser, and than let the user fill in the password for the zip file
(echo dummypassword; cat) | su dummyuser -c "unzip pwdprotectedfile.zip"
Here, the echo fills in the first part of the command, and then cat takes over to copy stdin to stdout to manually fill in the remaining part of whatever the command needs.
The (echo ; cat) method is the closest thing that (almost) works. But the problem here is that at the end an extra enter key press is needed to return to the command prompt.
How to do this properly without the extra key press needed?
The real situation I need this for is to run su -c somecommand, fill in the password automatically (from a secure source) and then let the user answers the questions asked by somecommand.
If your intent is to programmatically generate a content for the nano editor, it is as simple as telling nano to edit the standard input by specifying - as file name.
echo "blablabla" | nano -
Now consider that echo behaviour is not portable across shell versions, so prefer it printf '%s\n' "blablabla" for a single line of text.
To be courteous with the user, you can invoke his preferred editor as set in the EDITOR environment variable, with fall-back to vi if the EDITOR environment variable is not set.
printf '%s\n' "blablabla" | "${EDITOR:-vi}"
If you are using bash, you can replace piping printf with an here-string instead:
#!/usr/bin/env bash
"${EDITOR:-vi}" - <<<"blablabla"

Bash shell argument passing...?

You can use a semicolon in bash shell to specify multiple commands.
Sometimes, one of those commands pops a question, requiring user input. (typically 'y' / 'n', or whatever really)
If I know what I want to answer in advance, is there a way to parse it to the commands somehow, like an argument, or some weird magical pipe stuff?
You don't need any "weird magical pipe stuff", just a pipe.
./foo ; echo "y" | ./bar ; ./baz
Or magical herestring syntax if you prefer:
./foo ; ./bar <<<"y" ; ./baz
You can use the yes command to put a lot of 'y' 's to a pipe.
For example, if you want to remove all your text files, you can use
yes | rm -r *.txt
causing every question asked by rm being answered with a y.
If you want another default answer, you can give it as an argument to yes:
yes n | rm -r *.txt
This will output a 'n'.
For more information, see http://en.wikipedia.org/wiki/Yes_(Unix)
For the simple "yes" answer there is a command yes, available on most Unix and Linux platforms:
$ yes | /bin/rm -i *
For an advanced protocol you may want to check the famous Expect, also widely available. It needs basic knowledge of Tcl.
First, it's not bash popping these questions. It is the particular program called (for instance, cp -i asks before overwriting files). Frequently those commands also have switches to answer the questions, like -y for fsck, or how -f overrides -i in rm. Many programs could be answered through a pipe, which is why we have the command "yes", but some go to extra lengths to ensure they cannot; for instance ssh when asking for passwords. Also, there's nothing magical about pipes. If the program only sometimes asks a question, and it matters what question, there are tools designed for that such as "expect".
In a typical shell script, when you do know exactly what you want to feed in and the program accepts input on stdin, you could handle it using a plain pipe:
echo -e '2+2\n5*3' | bc
If it's a longer piece then a here document might be helpful:
bc <<EOF
2+2
3*5
EOF
Sometimes a command provides an option to set default answer to a question. One notable example is apt-get - a package manager for Debian/Ubuntu/Mint. It provides and options -y, --yes, --assume-yes to be used in non-interactive scripts.

Cron says "errors in crontab file, cannot install" [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I'm attempting to execute the following series of commands to create backups of MySQL databases.
When I attempt to add the command to my crontab using crontab -e I get the error "errors in crontab file, cannot install" and asks me if I want to retry.
mkdir /home/mysql-backup/`date '+%m-%d-%Y'`; mysql -s -r -e 'show databases' | while read db; do mysqldump $db -r /home/mysql-backup/`date '+%m-%d-%Y'`/${db}.sql; done; rm -r -f `date --date="1 week ago" +%m-%d-%Y`; du -k |sort -n > output; mail -s "MySQL Backups" "steven#brightbear.net" < output
Is there anything I should be changing in this file? Or should I look into creating a script file and calling that from cron.
Thanks in advance for any assistance you can provide.
If you gave that script to crontab -e of course it will disagree. A line in a crontab file should start with 5 fields indicating when you want the script to run, as can be read in crontab's manpage.
On the other hand, most Linux distros nowadays have preset facilities for things that should be executed hourly (/etc/cron.hourly), daily (/etc/cron.daily), etc. It's a whole lot easier to just put your script in a file in the appropriate directory and it will get executed in the selected time raster. An added advantage is that in these files you won't be forced to cram everything into one line.
Yes; as a matter of style, if nothing else, I encourage to put the SQL commands into a shell script, and then run the shell script from cron.  (And, as Anew points out, the command sequence is easier to maintain/debug if it’s broken out into multiple lines, with comments.)  But –– is that all of what you’re feeding into crontab?  Look at man crontab and add the fields that specify when you want the command to run.
From the crontab(5) man page, it looks like percent signs (%) have a special meaning, so that is probably where you're running into trouble.
Yes, you should put your commands into a separate shell script and just call that from the crontab line. This will also make it much easier to read the crontab file, and you can format your script nicely so that it's easier to maintain. And you can test it separately from the crontab that way.

su without password within a script

I'm sure this question has been answered before, but I can't find an answer that I like. I would like to write a shell script that executes a very specific script as another user (I want anyone to be able to start postgres as the postgres user). The script will have 710 perms so it will be executable by a certain group but not readable or writable by that group.
Now, I'm pretty sure there's no way to use 'su' without an interactive password prompt. There are lots of good reasons for that and I don't need to be convinced of the merit of those reasons (I'm told that someone savvier than me could grab the password off the processes list which is bad).
Question is, more generally how would I accomplish what I want to do without abusing unix security paradigms? Is there a way to allow user to execute a very specific process as another user?
This sort of situation is exactly what sudo was designed for.
You can create an executable (not a shell script) that launches the script that should run as the postgres user. Change the owner of the executable to the postgres user, and set the setuid bit.
See Best practice to run Linux service as a different user to address Celada's concern.
Well, you could use a simple script to access programmatically to an user using sudo and then execute all code you want.
Here is a simple script:
if [ "$#" -ne 2 ]; then
echo "Usage: "
echo " suprompt <user> <password>"
else
echo $2 | sudo -sS su $1
sudo su $1
fi
This script uses two arguments. The first one is the user you want to be, and the second arg is the password.
It works automatically.
You can change the final statement and do: sudo su $1 -c <command>
I hope this will work for you.

Resources