LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 06-26-2012, 03:12 AM   #1
Xenit
LQ Newbie
 
Registered: Jun 2012
Posts: 4

Rep: Reputation: Disabled
Question Bash script with su and here document ends prematurely


After spending quite some time reading and searching, I just don't get it.
Why does the following script NOT execute the echo command?
It does display the help text, but then it seems to end after exiting the ftp.
"tomcat" is any unix user. The script is executed as root.

Thanks in advance for enlightening me!

Code:
#!/bin/bash
su - tomcat <<EOF
ftp
help
quit
echo tatafoobar
EOF
 
Old 06-26-2012, 03:42 AM   #2
Jebram
LQ Newbie
 
Registered: May 2007
Location: Berlin, FRG
Distribution: Ubuntu
Posts: 22

Rep: Reputation: 4
Your use of "EOF" is confusing You.
It should mark the end of the Here-Is document,
not the end of the shell script file.
Besides: Please consider using "sudo" instead of "su". ("sudo" allows you to configure clean superuser access, look at the output of:
Quote:
man sudoers
, especially the EXAMPLES section)
For your script, try something like this:
Code:
#!/bin/bash
sudo -s tomcat <<END_OF_HEREIS_DOCUMENT
ftp
help
quit
END_OF_HEREIS_DOCUMENT

echo tatafoobar
# EOF

Last edited by Jebram; 06-26-2012 at 03:49 AM. Reason: added hint pointing at EXAMPLES section, sudoers man page is daunting.
 
Old 06-26-2012, 04:09 AM   #3
Xenit
LQ Newbie
 
Registered: Jun 2012
Posts: 4

Original Poster
Rep: Reputation: Disabled
Post

Thanks for the quick reply, Jebram!

You're quite right about sudo. I should change it.

As for the end marker EOF, the example is a bit simplified. The idea is that echo is a command which I want to execute as user tomcat.
In fact this is a snippet from a larger script, run as root.
I still don't see why the echo would not execute...
 
Old 06-26-2012, 04:37 AM   #4
jv2112
Member
 
Registered: Jan 2009
Location: New England
Distribution: Arch Linux
Posts: 719

Rep: Reputation: 106Reputation: 106
Thumbs up

This works fine for me. I took out the user since the way I read the man page it is not needed

Quote:
-s [command]
The -s (shell) option runs the shell specified by the SHELL environment variable if it is set or the shell as specified in the password database. If a command
is specified, it is passed to the shell for execution via the shell's -c option. If no command is specified, an interactive shell is executed.

Code:

#! /bin/bash
sudo -s  <<END_OF_HEREIS_DOCUMENT
clear
sl
figlet wow
END_OF_HEREIS_DOCUMENT

echo tatafoobar
 
Old 06-26-2012, 04:49 AM   #5
Xenit
LQ Newbie
 
Registered: Jun 2012
Posts: 4

Original Poster
Rep: Reputation: Disabled
Post

Again, the question is not so much about su or sudo, rather than why it does not execute the echo?!?
If instead of ftp, help, quit you just put another echo command, it works like a charm, ie it will print the two messsages and exit.
So it seems to be some problem with nested interactivity, but I just don't get the logic behind it.
For instance, this also works perfectly well and will indeed execute the echo tatafoobar:

Code:
#!/bin/bash
su - tomcat <<EOF
ftp << EOF2
help
quit
EOF2
echo tatafoobar
EOF
 
Old 06-26-2012, 05:07 AM   #6
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Perhaps there is something in tomcat's logon initialisation that consumes stdin. What happens if you omit su's - option?
 
Old 06-26-2012, 05:17 AM   #7
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by Xenit View Post
After spending quite some time reading and searching, I just don't get it.
Why does the following script NOT execute the echo command?
When you start the ftp program, it eats the rest of the here document as its input. In other words, the echo line gets read by the ftp command. No, there is no way to stop that from happening. The same would happen if you could type fast enough. (It happens in real life when logging out from an SSH connection, and typing the next command before the ssh application has time to exit.)

If you use
Code:
sudo -u "$USER" -- bash <<ENDOFCOMMANDS
    echo "Before the ftp command"
    ftp <<ENDOFFTP
help
quit
ENDOFFTP
    echo "After the ftp command"
ENDOFCOMMANDS
it works, because the command gets the inner here document as its input instead of the command stream.

Personally, I find the above here document syntax not only confusing, but error-prone, due to the funky quoting and evaluation rules you end up with. You're going to have to be a wizard in counting backslashes, if the here-document script uses variables. If the inner here-document uses shell variables.. well, I'm just sayin', I wouldn't. The backslashes start looking like a forest.

The best option is to split the script into privileged and per-user parts, in separate files. I prefer using this at the start of such scripts:
Code:
#!/bin/bash
[ "`id -u`" = "0" ] || exec sudo -u root -- "$0" "$@"
SCRIPTDIR="$(cd "$(dirname "$0")" && pwd -P || exit $?)" || exit $?
[ "$SCRIPTDIR/$(basename "$0")" -ef "$0" ] || exit $?
The second line makes sure the script has root privileges. If not, it asks for them.
The third line locates the physical directory this script file is located in.
The fourth line verifies that the file with the same name as the current script in that directory matches (has the same inode and device numbers) as "self", otherwise it aborts the script. Note that that should only happen when somebody renames or moves the directory at precisely the right moment.

After the above, regardless of the current working directory -- that is, the script can freely cd to anywhere else after that point --, you can run a sibling/child script under any user account using
Code:
sudo -u user -- "$SCRIPTDIR/sibling-script" arguments..
where sibling-script is the path to the sibling script relative to the directory this script is physically located in: if in the same directory, then just the sibling script name.

If none of the script parts actually need root privileges, only the ability to run the parts as specific users, you could create a dedicated local user, and allow suitable user accounts to run the main script as that user via sudo. Then, you also need to allow sudoing from that dedicated user, to the various sibling scripts, as the target users. It may sound complicated, but turns out to be quite robust in real life.

If you are seriously interested, I think I could provide a real life example scripts and sudoers snippets.

Last edited by Nominal Animal; 06-26-2012 at 05:19 AM.
 
1 members found this post helpful.
Old 06-26-2012, 05:51 AM   #8
Xenit
LQ Newbie
 
Registered: Jun 2012
Posts: 4

Original Poster
Rep: Reputation: Disabled
Thumbs up

Thanks for your reply, catkin. In fact, without the -s, same thing happens.

Thanks to Nominal Animal! I think he found a reasonable explanation. At least it seems plausible to me. I guess, internally, it ends up being a problem of who (which command) buffers the input sent by the here document. Thus the "ftp eating up the input". Excellent explanation!

As to your advice, I completely agree. In fact, the whole story is rather simple. Someone set up a central script in /etc/init.d to start / stop the whole Oracle infrastructure. On this particular machine, this is: 2 databases, one oracle EM Grid Control agent and one EM DB console.
In the end, the script is quite confusing, doing a lot of su to the oracle installation owner to start / stop the processes involved, and uses here documents mainly for SQL*Plus.
I had indeed thought about creating proper individual scripts, owner by the oracle user, and just using one singe su -c command (or sudo) in the main script.
The only question I wasn't really sure about was, where to put these scripts, as /etc/init.d might not be the best place to have non-root files or secondary scripts. (Any opinions?)
In fact this "whole Oracle infrastructure start / stop" script is also used to stop and then restart everything during backup at night. This way, we maintain only one script. I add this because maybe it influences to ideal location for the secondary scripts.
Of course, one obvious choice would be the oracle user's home directory, which is currently local, but maybe one day our sysadmin decides to move that to a network share and then we would have an added failure point for an emergency shutdown or, after such, a startup depending on the network share having started first. I guess really it's best to keep such unix homes local. So maybe it is the best location.

Cheers to Nominal Animal!

And again, THANKS!

Last edited by Xenit; 06-26-2012 at 05:54 AM.
 
Old 06-26-2012, 06:16 AM   #9
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Here is a test case you can use to check for yourself whether my explanation is correct:
Code:
#!/bin/bash
echo "Before bash"
bash <<ENDOFBASH
	echo "Before awk"
	awk '(\$1 == "exit") { exit(0) } 1'
first awk line
second awk line
third awk line; followed by the exit line
exit
	echo "After awk"
ENDOFBASH
echo "After bash"
Above, the awk command simulates the OP's ftp command. As per my explanation, the "After awk" gets consumed by the awk command.

Oh, and the backslash is necessary even if the awk commands are in single quotes. It is all in a here document, which means variable references etc. get evaluated first by the shell.

Now, edit the file, adding spaces and empty lines between the exit and echo "After awk". About 8000 bytes of spaces and newlines was enough for gawk-3.1.8. Since commands like awk don't normally buffer that much input at a time, it no longer consumes the echo "After awk" line. Just add the spaces and newlines until you see the After awk text in the output, to see for yourself.

I'm pretty confident my explanation is correct. It does explain all the behaviour I can see.

(BTW, healthy scepticism is healthy, especially when I'm doing the explanations. I never deceive, not intentionally, but I make more than my fair share of errors.)

Quote:
Originally Posted by Xenit View Post
The only question I wasn't really sure about was, where to put these scripts
The globally or root-only accessible base script in /usr/local/bin/, and the slave scripts in /usr/local/lib/site-oracle-init/.

If packaged into site-oracle-init*.{rpm,deb}, use /usr/bin/ and /usr/lib/site-oracle-init/, respectively.

In both cases you can hardcode SCRIPTDIR to /usr/local/lib/oracle-init/ and /usr/lib/site-oracle-init/, respectively.

If you think of a better name than site-oracle-init, just replace it in all of the above. This scheme is what other similar utilities do, and should be FHS (Filesystem Hieararchy Standard) compatible.

Quote:
Originally Posted by Xenit View Post
In fact this "whole Oracle infrastructure start / stop" script is also used to stop and then restart everything during backup at night. This way, we maintain only one script. I add this because maybe it influences to ideal location for the secondary scripts.
It does. I'd recommend putting the script itself in the lib dir too, with just a symlink to it from the /usr/local/bin/ or /usr/bin/. The symlink is needed so you don't need to type the full absolute path every time. I'd also put the general usage info into the script, if it gets executed with first parameter being -h or --help, and version if run with --version.

If your distribution has a /etc/sudoers.d/ directory, then you can include the necessary sudo configuration in /etc/sudoers.d/site-oracle-init file. (Use only alphanumerics, dashes and underscores in the name; sudo is very picky about those file names. Does not like full stops.)

Last edited by Nominal Animal; 06-26-2012 at 06:19 AM.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] Bash command execution timing weird or unrar shuts down script prematurely porphyry5 Programming 2 11-15-2011 12:20 PM
[SOLVED] Need bash script to check existence of images within an HTML document thunor Programming 7 10-26-2010 10:20 AM
bash: two very similar errors, one ends a while loop, the other doesn't openSauce Programming 2 12-02-2009 09:30 AM
linux SU command closing script prematurely. help please? Frelov Programming 4 02-20-2006 01:14 PM
while loop ending prematurely in a bash script meat-head Programming 7 05-08-2004 01:46 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 03:31 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration