Forums

Skip to content

Advanced search
  • Quick links
    • Unanswered topics
    • Active topics
    • Search
  • FAQ
  • Login
  • Register
  • Board index Assistance Portage & Programming
  • Search

Bash quoting hell

Problems with emerge or ebuilds? Have a basic programming question about C, PHP, Perl, BASH or something else?
Post Reply
Advanced search
8 posts • Page 1 of 1
Author
Message
m_a_xim
n00b
n00b
Posts: 32
Joined: Thu Oct 29, 2009 9:56 am

Bash quoting hell

  • Quote

Post by m_a_xim » Sun Mar 06, 2011 8:37 pm

Hi,
I love bash but there's one thing that always gets me and makes me lose tons of time: quotes!
I know quite a few tricks such as using "$@" "$*" and escape characters of course, however there are some odd cases when these aren't sufficient.
Here is my problem right now; I have this script:

Code: Select all

#!/bin/bash
sanitizeq() { echo $1 | sed -e 's/"/\\\\\\\"/g'; }
cmd="sg allownet \"$1 "
i=1
while shift; do echo $1; ((i++)); [[ -n "$1" ]] && cmd+="\\\"$(sanitizeq "$1")\\\" "; done
cmd+=\"
echo "$cmd"
eval "$cmd"
It's purpose is to launch a program as group 'allownet' however 'sg' only takes one argument: the whole command to execute and I want to make it possible to just send the arguments naturally separated. The 'echo's are for debugging purposes.
Here's an example:

Code: Select all

$ an echo hello "'my' \" world" #'an' is the script's name
hello
'my' " world

sg allownet "echo \"hello\" \"'my' \\\" world\" "
hello 'my' " world
works fine.
But this:

Code: Select all

$ an echo hello "'my' \\\" world"
hello
'my' \" world

sg allownet "echo \"hello\" \"'my' \\\\" world\" "
/home/mx/bin/an: eval: line 10: unexpected EOF while looking for matching `"'
/home/mx/bin/an: eval: line 11: syntax error: unexpected end of file
does not.

Of course I could add a clause to deal with '\"' in the 'sanitizeq' function but the code would still not work with '\\"', and this would go on ad infinitum. Yes, I could most certainly implement an algorithm to treat all cases but there must be a simpler way which is why I am asking for your advice.
This kind of problem is quite common when dealing with file names so it isn't a exactly extreme situation.
___________________________________
Top
Hu
Administrator
Administrator
Posts: 24385
Joined: Tue Mar 06, 2007 5:38 am

  • Quote

Post by Hu » Sun Mar 06, 2011 9:37 pm

According to the documentation, sg will pass its command back through /bin/sh, so all you need is to ensure that all the text arrives in a single element of argv. The problem is that you want the user to specify bad input (by underquoting the text on the command line) and then have the script try to guess what the user really meant. The solution is for the user to quote the input correctly. Since we know the text will go through another round of expansion when sg passes it to the shell, the user can probably just pass the entire first argument in a single quoted string. Try this:

Code: Select all

#!/bin/sh
set -x
sg allownet "$1"

Code: Select all

$ an a b c
+ sg allownet a
$ an 'a b c'
+ sg allownet 'a b c'
For working with filenames, quoting the variable at its expansion site will do the right thing, regardless of what characters are in the variable.
Top
m_a_xim
n00b
n00b
Posts: 32
Joined: Thu Oct 29, 2009 9:56 am

  • Quote

Post by m_a_xim » Sun Mar 06, 2011 10:04 pm

Thanks; I know I can do that but I'm looking to make the work of using the command easier by not having to quote everything. I want it to work like sudo (i.e. "sudo command arg1 arg2 ...").
___________________________________
Top
mv
Watchman
Watchman
User avatar
Posts: 6795
Joined: Wed Apr 20, 2005 12:12 pm

Re: Bash quoting hell

  • Quote

Post by mv » Mon Mar 07, 2011 8:41 am

m_a_xim wrote:

Code: Select all

sanitizeq() { echo $1 | sed -e 's/"/\\\\\\"/g'; }
First of all you should quote the $1 here.
Second: If $1 is -n you are lost. Better use printf '%s' "$1"
Third: Since you use $(sanitizeq ...) you will loose trailing newlines - they are cut off by $(...).
Fourth: Your strategy of quoting into "..." is bad, since in "..." variable substitution and un-quoting is still performed. A better strategy is to quote into '...', replacing the ' symbol by '\'''
Last but not least: Better do not use an external command like sed but the shell mechanisms for replacement like in bash ${var//...}
while shift; do ... ((i++)) ... $1;i done; echo $i
Such a thing is simply (and in POSIX) done as
for a
do ... "$a" ..
done
echo ${#}
Another story: Why do you want to use bash? I suggest to either use POSIX (so that it works on all systems) or - if compatibility is not an issue - to use zsh: The latter is really powerful and your problem is solved by simply applying the "q" (or even the "q-") flag to your "$@" array:

Code: Select all

su ... "${(q-)@}"
In POSIX there is no command for ${var//...}, but you can use ${var#*...} and ${var%%...*} in a loop. The function "Replace" which you can look up in /usr/bin/eix-functions (if you have eix installed) does this. After ". /usr/bin/eix-functions" you can use the following

Code: Select all

Push -c arg
for i
do Push arg "$i"
done
su ... "$arg"
An independent (smaller) version of the Push function can be found in the sudox script in the sudox.zip file (in bash you could write it shorter by using the ${var//...} substitution but again: If you rely on an incompatible shell anyway you are better off with using a more powerful shell like zsh).
Top
m_a_xim
n00b
n00b
Posts: 32
Joined: Thu Oct 29, 2009 9:56 am

  • Quote

Post by m_a_xim » Mon Mar 07, 2011 9:30 pm

Thanks Mv; you actually taught me a few nice tricks here.
I'm not too interested in zsh so I will just stick to bash. I did try it but there's just too much stuff to configure to my taste (e.g. vi mode works weird by default).
However, I still have a problem and it looks more like some kind of bug of deficient design in bash than anything else - but I'm probably just not getting something.

Code: Select all

#!/bin/bash
#squote() { printf '%s' "${1//\'/'\''}"; } # <----- BASH BUG???
squote() { printf '%s' "$1" | sed "s/'/'\\\''/g"; }
cmd="sg allownet '$1 "
shift
for i; do cmd+="$(squote \'"$(squote "$i")"\') "; done
cmd+=\'
echo "$cmd" 
sg allownet "$cmd"
The second line is what I tried to do using bash's substitution mechanism but there just doesn't seem to be any way of making it accept an odd number of single quotes while using the surrounding double quotes and taking the double quotes off just messes up printf's output.

Any ideas?

Oh, and I'm not sure what you mean exactly by your third point:
Third: Since you use $(sanitizeq ...) you will loose trailing newlines - they are cut off by $(...).
Do you mean the newlines are not conserved? How can I remedy this problem?
___________________________________
Top
m_a_xim
n00b
n00b
Posts: 32
Joined: Thu Oct 29, 2009 9:56 am

  • Quote

Post by m_a_xim » Fri Mar 11, 2011 4:46 pm

bump?
___________________________________
Top
mv
Watchman
Watchman
User avatar
Posts: 6795
Joined: Wed Apr 20, 2005 12:12 pm

  • Quote

Post by mv » Sat Mar 12, 2011 8:19 am

m_a_xim wrote:I'm not too interested in zsh so I will just stick to bash. I did try it but there's just too much stuff to configure to my taste (e.g. vi mode works weird by default).
Yes, I agree: The default configuration of zsh is rather strange. However, when one takes the time to configure it, nothing can beat it.
The second line is what I tried to do using bash's substitution mechanism but there just doesn't seem to be any way of making it accept an odd number of single quotes while using the surrounding double quotes
It seems to work if you make the content of the substitution a variable - then you do not need such a clumsy syntax. However, such things might depend on the bash version which is why I prefer to use POSIX over some incompatible extensions.
Do you mean the newlines are not conserved? How can I remedy this problem?
Trailing newlines are not conserved:

Code: Select all

$ echo "$(echo 'A
')"B
AB
This is a "feature" of $(...) and conforms to POSIX standard. The simplest solution is to avoid using $(...) which is not needed in your case (you can store the result of a substitution directly in a variable). For problems where you must use $(...) you can make sure that there is no trailing newline by e.g. appending an "x" and then cutting off that "x" again. Something like:

Code: Select all

A="$(.... ; echo x)"
A="${A%x}"
Top
m_a_xim
n00b
n00b
Posts: 32
Joined: Thu Oct 29, 2009 9:56 am

  • Quote

Post by m_a_xim » Sat Mar 12, 2011 4:06 pm

Thank you.
Using variables, here is my final code:

Code: Select all

#!/bin/bash
quote=\'
escquote=\'\\\'\'
squote() { printf '%s' "${1//$quote/$escquote}"; }
#squote() { printf '%s' "$1" | sed "s/'/'\\\''/g"; } 
cmd="sg allownet '$1 "
shift
for i; do cmd+="$(squote \'"$(squote "$i")"\') "; done
cmd+=\'
echo "$cmd"
sg allownet "$cmd"
In retrospect, this is pretty crazy for something so simple.

And the information about $(...) will probably avoid me many headaches.
___________________________________
Top
Post Reply

8 posts • Page 1 of 1

Return to “Portage & Programming”

Jump to
  • Assistance
  • ↳   News & Announcements
  • ↳   Frequently Asked Questions
  • ↳   Installing Gentoo
  • ↳   Multimedia
  • ↳   Desktop Environments
  • ↳   Networking & Security
  • ↳   Kernel & Hardware
  • ↳   Portage & Programming
  • ↳   Gamers & Players
  • ↳   Other Things Gentoo
  • ↳   Unsupported Software
  • Discussion & Documentation
  • ↳   Documentation, Tips & Tricks
  • ↳   Gentoo Chat
  • ↳   Gentoo Forums Feedback
  • ↳   Duplicate Threads
  • International Gentoo Users
  • ↳   中文 (Chinese)
  • ↳   Dutch
  • ↳   Finnish
  • ↳   French
  • ↳   Deutsches Forum (German)
  • ↳   Diskussionsforum
  • ↳   Deutsche Dokumentation
  • ↳   Greek
  • ↳   Forum italiano (Italian)
  • ↳   Forum di discussione italiano
  • ↳   Risorse italiane (documentazione e tools)
  • ↳   Polskie forum (Polish)
  • ↳   Instalacja i sprzęt
  • ↳   Polish OTW
  • ↳   Portuguese
  • ↳   Documentação, Ferramentas e Dicas
  • ↳   Russian
  • ↳   Scandinavian
  • ↳   Spanish
  • ↳   Other Languages
  • Architectures & Platforms
  • ↳   Gentoo on ARM
  • ↳   Gentoo on PPC
  • ↳   Gentoo on Sparc
  • ↳   Gentoo on Alternative Architectures
  • ↳   Gentoo on AMD64
  • ↳   Gentoo for Mac OS X (Portage for Mac OS X)
  • Board index
  • All times are UTC
  • Delete cookies

© 2001–2026 Gentoo Foundation, Inc.

Powered by phpBB® Forum Software © phpBB Limited

Privacy Policy

 

 

magic