building strings with variable input

O

Olaf Meyer

Sometimes if find it clumsy unsing the following approach building strings:

cmd = "%s -start %s -end %s -dir %s" % (executable, startTime, endTime,
directory)

Especially if you have a lot of variable input it makes it hard to match
the variables to the proper fields. From other scripting languanges I'm
used to something like:

$cmd = "$executable -start $startTime -end $endTime -dir $directory"

This makes it very easy to see how the string is actually built. You
dont't have to worry where which variables go.

Is there a similar way to do this in python?

Thanks,
Olaf
 
E

Erik Max Francis

Olaf said:
Especially if you have a lot of variable input it makes it hard to
match
the variables to the proper fields. From other scripting languanges
I'm
used to something like:

$cmd = "$executable -start $startTime -end $endTime -dir $directory"

This makes it very easy to see how the string is actually built. You
dont't have to worry where which variables go.

Is there a similar way to do this in python?

Sure:

cmd = "%(executable)s -start %(startTime)s -end %(endTime)s -dir
%(directory)s" % locals()

There are also more expansive solutions such as YAPTU or EmPy.

Note, however, that what you are trying to do (presuming you're passing
this to os.system or something similar) is potentially a serious
security risk. If the values of the strings you are constructing the
command line are not fully trustworthy, they can be easily manipulated
to make your program execute arbitrary shell commands.
 
P

Peter Otten

Olaf said:
Sometimes if find it clumsy unsing the following approach building
strings:

cmd = "%s -start %s -end %s -dir %s" % (executable, startTime, endTime,
directory)

Especially if you have a lot of variable input it makes it hard to match
the variables to the proper fields. From other scripting languanges I'm
used to something like:

$cmd = "$executable -start $startTime -end $endTime -dir $directory"

This makes it very easy to see how the string is actually built. You
dont't have to worry where which variables go.

Is there a similar way to do this in python?
'from X to Y'

or even
'from A to B'

Peter
 
O

Olaf Meyer

Erik said:
Olaf Meyer wrote:




Sure:

cmd = "%(executable)s -start %(startTime)s -end %(endTime)s -dir
%(directory)s" % locals()

There are also more expansive solutions such as YAPTU or EmPy.

Note, however, that what you are trying to do (presuming you're passing
this to os.system or something similar) is potentially a serious
security risk. If the values of the strings you are constructing the
command line are not fully trustworthy, they can be easily manipulated
to make your program execute arbitrary shell commands.

Erik,

thanks for your solution suggestion and pointing out the security risks.
However security is not an issue in my case ;-)

Olaf
 
D

David M. Cooke

At some point said:
Sure:

cmd = "%(executable)s -start %(startTime)s -end %(endTime)s -dir
%(directory)s" % locals()

There are also more expansive solutions such as YAPTU or EmPy.

Note, however, that what you are trying to do (presuming you're passing
this to os.system or something similar) is potentially a serious
security risk. If the values of the strings you are constructing the
command line are not fully trustworthy, they can be easily manipulated
to make your program execute arbitrary shell commands.

In which case he's probably better off with his original format (almost):

cmd = '"$executable" -start "$startTime" -end "$endTime" -dir "$directory"'
os.environ['executable'] = 'blah'
os.environ['startTime'] = '12'
os.environ['endTime'] = '18'
os.environ['directory'] = './'
os.system(cmd)

This way, the shell handles all the quoting. You can do
del os.environ['executable']
afterwards to clean up. I got this technique from
http://freshmeat.net/articles/view/337/

For the quoting, compare:
os.environ['string'] = "`uname` $TERM"
os.system('echo "$string"')
`uname` $PATH
(this is what we want: don't run arbitrary commands or expand
environment variables given in a user string)

withLinux xterm
(whoops, security leak)
 
O

Olaf Meyer

Erik said:
Olaf Meyer wrote:




Sure:

cmd = "%(executable)s -start %(startTime)s -end %(endTime)s -dir
%(directory)s" % locals()

There are also more expansive solutions such as YAPTU or EmPy.

Note, however, that what you are trying to do (presuming you're passing
this to os.system or something similar) is potentially a serious
security risk. If the values of the strings you are constructing the
command line are not fully trustworthy, they can be easily manipulated
to make your program execute arbitrary shell commands.

I just found out another way ;-) Using the locals() has the disadvantage
that I cannot use more complex variable parameters (e.g. certain values
of a dictionary). The following works well:

cmd = (executable + " -start " + startTime + " -end " + endTime +
" -dir " + options.dir)

Olaf
 
E

Erik Max Francis

David M. Cooke said:
In which case he's probably better off with his original format
(almost):

cmd = '"$executable" -start "$startTime" -end "$endTime" -dir \
"$directory"'
os.environ['executable'] = 'blah'
os.environ['startTime'] = '12'
os.environ['endTime'] = '18'
os.environ['directory'] = './'
os.system(cmd)

This doesn't resolve the underlying possibility for mailicious people in
control of the contents of those variables to get it to execute
arbitrary shell code. (In his case he says it isn't an issue, but
still.)
 
D

David M. Cooke

At some point said:
David M. Cooke said:
In which case he's probably better off with his original format
(almost):

cmd = '"$executable" -start "$startTime" -end "$endTime" -dir \
"$directory"'
os.environ['executable'] = 'blah'
os.environ['startTime'] = '12'
os.environ['endTime'] = '18'
os.environ['directory'] = './'
os.system(cmd)

This doesn't resolve the underlying possibility for mailicious people in
control of the contents of those variables to get it to execute
arbitrary shell code. (In his case he says it isn't an issue, but
still.)

Do you mean something like
os.environ['startTime'] = '`rm -rf /`'
?
That 'rm -rf /' *won't* be executed: the shell will expand
"$startTime" to "`rm -rf /`", and that's it. Of course, if the
executable you're calling is a shell script that doesn't handle it's
arguments correctly, then you're in trouble. That means $executable is
bad practice -- you're allowing arbitrary commands to be called.
 
E

Erik Max Francis

David M. Cooke said:
Do you mean something like
os.environ['startTime'] = '`rm -rf /`'
?

No, I mean something like

os.environ['startTime'] = '"; rm -rf /; : "'

The lesson to be learned here is: Do not build shell commands from
untrusted inputs. Ever.
 
D

David M. Cooke

At some point said:
David M. Cooke said:
Do you mean something like
os.environ['startTime'] = '`rm -rf /`'
?

No, I mean something like

os.environ['startTime'] = '"; rm -rf /; : "'

The lesson to be learned here is: Do not build shell commands from
untrusted inputs. Ever.

Doesn't work:
os.environ['string'] = '"; uname; : "'
os.system('echo "$string"')
"; uname; : "

Although the advice of not building shell commands is still prudent;
just because none of mine or your methods to defeat haven't worked,
doesn't mean there isn't a technique that will.

It's also dependent on having a good shell -- I'm using bash 2.05b.0.
 
T

Tim Roberts

Olaf Meyer said:
I just found out another way ;-) Using the locals() has the disadvantage
that I cannot use more complex variable parameters (e.g. certain values
of a dictionary). The following works well:

cmd = (executable + " -start " + startTime + " -end " + endTime +
" -dir " + options.dir)

Yes, that works, but you should bear in mind that it is slower than the %s
option. The "+" operations are all separate interpreter steps, while the
"%" operation is done in C.

At least, it was that way when I asked this same question several years
ago. If it has changed, I'm sure someone will point out my error.

Sometimes, it can make sense to write it this way:

cmd = ' '.join((
executable,
"-start", startTime,
"-end", endTime,
"-dir", options.dir
))
 
P

Paul McGuire

Tim Roberts said:
Yes, that works, but you should bear in mind that it is slower than the %s
option. The "+" operations are all separate interpreter steps, while the
"%" operation is done in C.

On the relative time scales of concatenating 7 strings compared to forking
off a separate process (which I presume is what is to be done with cmd), I'd
go for the more readable representation, to aid in long term
maintainability.

If I have some string concatenation being done in a highly repetitive part
of code, then by all means, replace it with one of the half dozen documented
optimized alternatives. But if I build a string in order to create a
sub-process, or invoke a database query, or make a remote CORBA invocation,
etc., then these "optimizations" don't really save much time, and instead
distract me/reviewers/testers/maintainers from the important program logic.

-- Paul
 
D

Dave Benjamin

Sometimes if find it clumsy unsing the following approach building strings:

cmd = "%s -start %s -end %s -dir %s" % (executable, startTime, endTime,
directory)

Especially if you have a lot of variable input it makes it hard to match
the variables to the proper fields. From other scripting languanges I'm
used to something like:

$cmd = "$executable -start $startTime -end $endTime -dir $directory"

This makes it very easy to see how the string is actually built. You
dont't have to worry where which variables go.

Is there a similar way to do this in python?

Go here:
http://lfw.org/python/

Look under "string interpolation for Python".

Examples supported:

"Here is a $string."
"Here is a $module.member."
"Here is an $object.member."
"Here is a $functioncall(with, arguments)."
"Here is an ${arbitrary + expression}."
"Here is an $array[3] member."
"Here is a $dictionary['member']."

Thanks to Ka-Ping Yee! I've succesfully used this to build a homebrew
templating language. It's nice and lightweight.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top