LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Software (https://www.linuxquestions.org/questions/linux-software-2/)
-   -   Yet Another Bash Quotes Within Quotes Issue (https://www.linuxquestions.org/questions/linux-software-2/yet-another-bash-quotes-within-quotes-issue-791073/)

tboyer 02-23-2010 02:41 PM

Yet Another Bash Quotes Within Quotes Issue
 
This one's... perplexing.

I've got a simple script that I'm trying to email error messages from. I want to do something like this:

MAIL="/bin/mailx -s Error from $(basename $0) root"
/bin/umount /data_backup||{ echo "backup unmount failed"|$MAIL;exit 1; }

but of course that won't work, because it'll try to mail a message with the subject 'Error' to the user 'from', 'daily_backups', and 'root'. So the question is how to get quotes around the subject.

The obvious is to escape a quote or double-quote. But that gives me:

MAIL="/bin/mailx -s \'Error from $(basename $0)\' root"

which the shell expands to

+ /bin/mailx -s '\'\''Error' from 'daily_file_backup\'\''' root

which doesn't work, or

MAIL="/bin/mailx -s \"Error from $(basename $0)\" root"

expanding to

+ /bin/mailx -s '"Error' from 'daily_file_backup"' root
[root@dg scripts]# daily_file_backup"... Unbalanced '"'

I've tried replacing the whole subject with another variable, i.e.

SUBJ="Error from $(basename $0)"
MAIL="/bin/mailx -s "$SUBJ" root"

with no luck. Various combinations of quotes and escapes fail miserably with that, also:

MAIL="/bin/mailx -s /"$SUBJ/" root"
+ /bin/mailx -s '"Error' from 'daily_file_backup"' root
[root@dg scripts]# daily_file_backup"... Unbalanced '"'

Now the strange part is - with debugging on, the string _looks_ like it's OK:

+ SUBJ='Error from daily_file_backup'
+ MAIL='/bin/mailx -s "Error from daily_file_backup" root'

which is exactly what I want $MAIL to be. But further on, when it actually executes:

+ /bin/mailx -s '"Error' from 'daily_file_backup"' root

Driving me nuts. Any pointers in the right direction appreciated.

pixellany 02-23-2010 03:57 PM

Sorry, but I can't follow all that.

There are two secrets to quoting:
1. The "hard quote" (') protects everything inside, whereas the "soft" quote ("), allows certain things--eg variables-- to be expanded by the shell.

2. Quotes behave like "toggles" so, to turn off quoting for part of an expression, you just do as follows:
'stuff and 'unquoted stuff' more quoted stuff' ("unquoted stuff" might look like it was being quoted doubly, but it's just being UN-quoted.

Beyond this, I've never had any luck with anything except trial and error.

Elv13 02-23-2010 10:14 PM

Cheapest last ressort solution:

VAR="cmd1 $1 `echo -n 'my text'`"

but normally, just going in and out of quotation work

VAR=$VAR2"some text "$VAR3'some more text'

dive 02-23-2010 10:59 PM

I think using vars like that you are just overcomplicating things.

/bin/umount /data_backup || echo "backup unmount failed" | /bin/mailx -s "Error from $(basename $0)" root

tboyer 02-24-2010 07:00 AM

>> I think using vars like that you are just overcomplicating things.

>> /bin/umount /data_backup || echo "backup unmount failed" | /bin/mailx -s "Error from $(basename $0)" root

Yeah, you'd think so, wouldn't you? But it doesn't work:

root@dg ~]# /bin/umount /data_backup || echo "backup unmount failed" | /bin/mailx -s "Error from $(basename $0)" root
umount: /data_backup: device is busy
umount: /data_backup: device is busy
basename: invalid option -- b
Try `basename --help' for more information.

and the email comes back with the subject "Error from ".

I can do a plain $0 in there, and it works - and that's my workaround, I guess.

Skaperen 02-24-2010 07:36 AM

You used:
Code:

MAIL="/bin/mailx -s \'Error from $(basename $0)\' root"
However, you don't need to escape a different kind of quote. You don't need to escape single quotes inside double quotes. So try:
Code:

MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
That will give you something like:
Code:

/bin/mailx -s 'Error from foo' root
as the string value in variable MAIL. The $(basename $0) part will be interpreted at the time of assignment to MAIL (e.g. using $0 from the command that did this assignment). If that's what you want, you should be good to go with this.

tboyer 02-24-2010 07:42 AM

Quote:

Originally Posted by Skaperen (Post 3875004)
You used:
Code:

MAIL="/bin/mailx -s \'Error from $(basename $0)\' root"
However, you don't need to escape a different kind of quote. You don't need to escape single quotes inside double quotes. So try:
Code:

MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
That will give you something like:
Code:

/bin/mailx -s 'Error from foo' root
as the string value in variable MAIL. The $(basename $0) part will be interpreted at the time of assignment to MAIL (e.g. using $0 from the command that did this assignment). If that's what you want, you should be good to go with this.

Nope; because the single quote won't expand the $(basename $0). It comes out

MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
[root@dg scripts]# echo $MAIL
/bin/mailx -s 'Error from ' root

tboyer 02-24-2010 08:33 AM

I think I've determined that the only easy way to do this is split it up:

SUBJ="Error from $(basename $0)"
MAIL="/bin/mailx"

/bin/umount /data_backup|| \
{ $ECHO "backup unmount failed"|$MAIL -s "$SUBJ" root;exit 1; }


works.

dive 02-24-2010 08:35 AM

Try "basename ${0/-//}"

Any good?

Skaperen 02-24-2010 09:40 AM

Quote:

Originally Posted by tboyer (Post 3875006)
Nope; because the single quote won't expand the $(basename $0). It comes out

MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
[root@dg scripts]# echo $MAIL
/bin/mailx -s 'Error from ' root

Hmmm ... it expands it for me:
Code:

altair/phil /home/phil 141> cat /home/phil/cmd/foo
#!/bin/bash
MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
echo "MAIL='${MAIL}'" 1>&2
altair/phil /home/phil 142> /home/phil/cmd/foo
MAIL='/bin/mailx -s 'Error from foo' root'
altair/phil /home/phil 143>

Of course, if you reverse the quotes used, it will do as you describe:
Code:

altair/phil /home/phil 143> cat /home/phil/cmd/bar
#!/bin/bash
MAIL='/bin/mailx -s "Error from $(basename $0)" root'
echo "MAIL='${MAIL}'" 1>&2
altair/phil /home/phil 144> /home/phil/cmd/bar
MAIL='/bin/mailx -s "Error from $(basename $0)" root'
altair/phil /home/phil 145>

So I wonder how you are getting it to not expand if this is truly bash.

tboyer 02-25-2010 08:31 AM

Quote:

Originally Posted by Skaperen (Post 3875132)
Hmmm ... it expands it for me:
Code:

altair/phil /home/phil 141> cat /home/phil/cmd/foo
#!/bin/bash
MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
echo "MAIL='${MAIL}'" 1>&2
altair/phil /home/phil 142> /home/phil/cmd/foo
MAIL='/bin/mailx -s 'Error from foo' root'
altair/phil /home/phil 143>

Of course, if you reverse the quotes used, it will do as you describe:
Code:

altair/phil /home/phil 143> cat /home/phil/cmd/bar
#!/bin/bash
MAIL='/bin/mailx -s "Error from $(basename $0)" root'
echo "MAIL='${MAIL}'" 1>&2
altair/phil /home/phil 144> /home/phil/cmd/bar
MAIL='/bin/mailx -s "Error from $(basename $0)" root'
altair/phil /home/phil 145>

So I wonder how you are getting it to not expand if this is truly bash.

OK, something very, very strange is going on here. Bog-standard RHEL5.4 system.

[root@dg ~]# env
HOSTNAME=dg.denmantire.com
SHELL=/bin/bash
TERM=vt220
HISTSIZE=1000
USER=root
...

so I'm using bash, right?

Watch this:

[root@dg ~]# MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
basename: invalid option -- b
Try `basename --help' for more information.
[root@dg ~]# echo $MAIL
/bin/mailx -s 'Error from ' root
[root@dg ~]# /bin/bash
[root@dg ~]# MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
[root@dg ~]# echo $MAIL
/bin/mailx -s 'Error from bash' root
[root@dg ~]#

Not machine-specific, either. I've got a dozen systems in three locations - all do exactly the same thing. Let's assume I'm not totally p0wned... :)

Has to be something weird in a .profile somewhere. I'll track this down now, only because it'll drive me nuts.

catkin 02-25-2010 10:29 AM

Quote:

Originally Posted by tboyer (Post 3876353)
[root@dg ~]# MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
basename: invalid option -- b
Try `basename --help' for more information.
[root@dg ~]# echo $MAIL
/bin/mailx -s 'Error from ' root
[root@dg ~]# /bin/bash
[root@dg ~]# MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
[root@dg ~]# echo $MAIL
/bin/mailx -s 'Error from bash' root

Try echo $0 so we can see what it is

tboyer 02-25-2010 10:35 AM

Quote:

Originally Posted by catkin (Post 3876454)
Try echo $0 so we can see what it is

[root@dg ~]# MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
basename: invalid option -- b
Try `basename --help' for more information.
[root@dg ~]# echo $0
-bash
[root@dg ~]# /bin/bash
[root@dg ~]# MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
[root@dg ~]# echo $0
/bin/bash

Hmmm. Never noticed that before - what's -bash?

tboyer 02-25-2010 11:49 AM

Quote:

Originally Posted by tboyer (Post 3876458)
[root@dg ~]# MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
basename: invalid option -- b
Try `basename --help' for more information.
[root@dg ~]# echo $0
-bash
[root@dg ~]# /bin/bash
[root@dg ~]# MAIL="/bin/mailx -s 'Error from $(basename $0)' root"
[root@dg ~]# echo $0
/bin/bash

Hmmm. Never noticed that before - what's -bash?

Duh. That's it, of course. If my shell is -bash, basename is returning -b and seeing it as an option flag.

tboyer 02-25-2010 11:51 AM

Quote:

Originally Posted by dive (Post 3875062)
Try "basename ${0/-//}"

Any good?

Works great.

Now, tell me why. :)


All times are GMT -5. The time now is 06:22 PM.