`echo %!(*`

Hello all,

Does anyone know a way to do backtick interpolation while protecting the
command’s arguments from the shell, or bypassing the shell entirely? I’m
writing a script to rename badly named music files, and I have to pass the
bad name to vorbiscomment and read it’s output.

Apparently Kernel::` is a method, but I can’t figure out how to call it
with my own arguments. Is there a non-punctuation name for it? This
would only be useful if it could accept an argument list as an array and
not use the shell, as Kernel::system does.

Thanks,

···


Tom Felker, tcfelker@mtco.com
http://vlevel.sourceforge.net - Stop fiddling with the volume knob.

I’m not comfortable with an idea that’s not constantly being challenged.

I’m not sure I understand what you’re asking about backtick
interpolation. Can you give me a simple example of what you’d like the
backtick to do so I understand beter?

As for renaming a file, would File.rename do what you want? :

dcarrera ~ $ ls *.mp3
BadlyNamedFile.mp3
dcarrera ~ $ ruby -e ‘File.rename(“BadlyNamedFile.mp3”, “NewName.mp3”)’
dcarrera ~ $ ls *.mp3
NewName.mp3

···

On Mon, Sep 01, 2003 at 07:46:24AM +0900, Tom Felker wrote:

Hello all,

Does anyone know a way to do backtick interpolation while protecting the
command’s arguments from the shell, or bypassing the shell entirely? I’m
writing a script to rename badly named music files, and I have to pass the
bad name to vorbiscomment and read it’s output.

Apparently Kernel::` is a method, but I can’t figure out how to call it
with my own arguments. Is there a non-punctuation name for it? This
would only be useful if it could accept an argument list as an array and
not use the shell, as Kernel::system does.

Thanks,

Tom Felker, tcfelker@mtco.com
http://vlevel.sourceforge.net - Stop fiddling with the volume knob.

I’m not comfortable with an idea that’s not constantly being challenged.


Daniel Carrera, Math PhD student at UMD. PGP KeyID: 9AF77A88
.-“~~~”-.
/ O O \ ATTENTION ALL PASCAL USERS:
: s :
\ _/ / To commemorate the anniversary of Blaise Pascal’s
`-.
_.-’ birth (today) all your programs will run at half speed.

“Tom Felker” tcfelker@mtco.com wrote in message

Does anyone know a way to do backtick interpolation while protecting the
command’s arguments from the shell, or bypassing the shell entirely?

I am not sure that I understand what you mean by “bypassing the shell”,
but will this work for you:

a=“%!(*”
%x{echo #{a}}

writing a script to rename badly named music files, and I have to pass the
bad name to vorbiscomment and read it’s output.

What is a “vorbiscomment” ?

Apparently Kernel::` is a method, but I can’t figure out how to call it
with my own arguments. Is there a non-punctuation name for it? This

Again, I do not understand this “call with my own arguments” part. Is this
some *ix lingo ? Pardon my ignorance.

would only be useful if it could accept an argument list as an array and
not use the shell, as Kernel::system does.

At least on Windows, both system and backtick use the shell…I think :wink:

Thanks,

Tom Felker, tcfelker@mtco.com

HTH,
– shanko

You can call it explicitly like this:

Kernel.send(:`, "ls /etc")

However unlike system, backtick doesn’t let you pass a line already broken
into arguments:

Kernel.send(:`, "ls", "/etc")
#>> ArgumentError: wrong number of arguments(2 for 1)

so that doesn’t help you.

Regards,

Brian.

···

On Mon, Sep 01, 2003 at 07:46:24AM +0900, Tom Felker wrote:

Apparently Kernel::` is a method, but I can’t figure out how to call it
with my own arguments. Is there a non-punctuation name for it?

oldName = “02_Ship_At_Sea_(Instrumental).ogg”
tags = Hash.new
vorbiscomment #{oldName}.each_line do |line|
key, value = line.split(“=”, 2)
tags[key.downcase] = value
end

…use tags to formulate proper filename and rename

Errors:

sh: -c: line 1: syntax error near unexpected token (' sh: -c: line 1: vorbiscomment 02_Ship_At_Sea_(Instrumental).ogg’

If you use system, you can pass the arguments in an array, and the shell
isn’t used, so you can have arguments that the shell would barf on.

system(“echo !@#$%^&”) == “”
system(“echo”, “!@#$%^&”) == “!@#$%^&”)

I need a backtick method that works the same way. Having popen work
that way would be nice. As it is, I think it’s only possible with
popen(“-”), having the child exec in such a way as to not interpolate
the arguments, and reading from that.

···

On Mon, 01 Sep 2003 07:51:55 +0900, Daniel Carrera wrote:

I’m not sure I understand what you’re asking about backtick
interpolation. Can you give me a simple example of what you’d like the
backtick to do so I understand beter?


Tom Felker, tcfelker@mtco.com
http://vlevel.sourceforge.net - Stop fiddling with the volume knob.

ruby -r complex -e ‘c,m,w,h=Complex(-0.75,0.136),50,150,100;puts"P6\n#{w}\n#{h}
n255";(0…h).each{|j|(0…w).each{|i|n,z=0,Complex(.9i/w,.9j/h);while n<=m&&(
z-c).abs<=2;z=zz+c;n+=1 end;print [10+n15,0,rand99].pack("C")}}’|display
#by Michael Neumann AFAICT

It’s simple. When you execute some command (or the equivalent
using %x), what Ruby actually executes is your command interpreter
(“shell” in Unix-speak), passing it the string between ...
to execute. It’s up to the shell to split up the command into
words, handle wildcard expansion (at least on UNIX; the command
interpreter on Windows leaves that latter duty up to the individual
commands), etc. The problem is that all this power is a security
hole because it’s easy to trick command interpreters into doing
ugly things just by passing something nefarious as a “filename”.

The original poster mentioned Kernel.system, which is the way to
execute a command when you don’t care about its output.
If you pass it one long string, Kernel.system will also invoke the
shell, but if instead you pass it separate arguments for each word of
the command line, it bypasses the shell and executes the command
directly. Thus:

system("ps -fp#{$$}")		# also invokes the shell
system('ps', "-fp#{$$}")	# doesn't invoke the shell
psOutput = `ps -fp#{$$}`	# invokes the shell

The question is: how do you complete the list, that is, capture the
command output without involving the shell?

The only solution of which I’m aware is to use popen/exec:

if fd = IO.popen('-') then
    psOutput = fd.readlines.join("\n")
else
    exec 'ps', "-fp#{$$}"
end

Of course, you could turn this into a method:

def safeBackticks(*args)
    if fd = IO.popen('-') then
	output = fd.readlines.join("\n")
    else
	exec *args
    end
    return output
end
···

On Sun, Aug 31, 2003 at 06:00:34PM -0500, Shashank Date wrote:

I am not sure that I understand what you mean by “bypassing the shell”,

I am not sure that I understand what you mean by “bypassing the shell”,
but will this work for you:

UNIX shells do globbing, whereas DOS (and CMD) leave that for the program to
do the globbing. Let’s say you have Sound1.ogg and Sound2.ogg in a
directory. When a DOS program gets a command like:

vorbiscomment *.ogg

ARGV[0] is “*.ogg”. It’s up to vorbiscomment to convert *.ogg into all of
the files in the directory that match *.ogg. However, when a UNIX shell gets
the same command, ARGV[0] is “Sound1.ogg”; ARGV[1] is “Sound2.ogg”.

Apparently Kernel::` is a method, but I can’t figure out how to call it
with my own arguments. Is there a non-punctuation name for it? This

Kernel::system(command [, args*]) should do what you’re after. If you know
you’re on a Unix system, you can escape asterisks with a backslash (e.g.,
“\\*” in Ruby). That should work.

-austin

···

On Mon, 1 Sep 2003 08:06:30 +0900, Shashank Date wrote:

austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.08.31
* 20.27.52

Try this:

  • vorbiscomment #{oldName}.each_line do |line|
  • vorbiscomment "#{oldName}".each_line do |line|

The quotes should make the shell interpret the name literally, instead of
seeing the brackets as a token.

Cheers,
Daniel.

···

On Mon, Sep 01, 2003 at 08:46:33AM +0900, Tom Felker wrote:

On Mon, 01 Sep 2003 07:51:55 +0900, Daniel Carrera wrote:

I’m not sure I understand what you’re asking about backtick
interpolation. Can you give me a simple example of what you’d like the
backtick to do so I understand beter?

oldName = “02_Ship_At_Sea_(Instrumental).ogg”
tags = Hash.new
vorbiscomment #{oldName}.each_line do |line|
key, value = line.split(“=”, 2)
tags[key.downcase] = value
end

…use tags to formulate proper filename and rename

Errors:

sh: -c: line 1: syntax error near unexpected token (' sh: -c: line 1: vorbiscomment 02_Ship_At_Sea_(Instrumental).ogg’

If you use system, you can pass the arguments in an array, and the shell
isn’t used, so you can have arguments that the shell would barf on.

system(“echo !@#$%^&”) == “”
system(“echo”, “!@#$%^&”) == “!@#$%^&”)

I need a backtick method that works the same way. Having popen work
that way would be nice. As it is, I think it’s only possible with
popen(“-”), having the child exec in such a way as to not interpolate
the arguments, and reading from that.


Tom Felker, tcfelker@mtco.com
http://vlevel.sourceforge.net - Stop fiddling with the volume knob.

ruby -r complex -e ‘c,m,w,h=Complex(-0.75,0.136),50,150,100;puts"P6\n#{w}\n#{h}
n255";(0…h).each{|j|(0…w).each{|i|n,z=0,Complex(.9i/w,.9j/h);while n<=m&&(
z-c).abs<=2;z=zz+c;n+=1 end;print [10+n15,0,rand99].pack("C")}}’|display
#by Michael Neumann AFAICT


Daniel Carrera, Math PhD student at UMD. PGP KeyID: 9AF77A88
.-“~~~”-.
/ O O \ ATTENTION ALL PASCAL USERS:
: s :
\ _/ / To commemorate the anniversary of Blaise Pascal’s
`-.
_.-’ birth (today) all your programs will run at half speed.

Your error is a shell syntax error, and not a Ruby error. Unix shells
don’t recognize unescaped parentheses within file names.

Change the line

oldName = “02_Ship_At_Sea_(Instrumental).ogg”

to oldName = “02_Ship_At_Sea_(Instrumental).ogg”

···

On Sunday, August 31, 2003, at 07:46 PM, Tom Felker wrote:

oldName = “02_Ship_At_Sea_(Instrumental).ogg”
tags = Hash.new
vorbiscomment #{oldName}.each_line do |line|
key, value = line.split(“=”, 2)
tags[key.downcase] = value
end

…use tags to formulate proper filename and rename

Errors:

sh: -c: line 1: syntax error near unexpected token (' sh: -c: line 1: vorbiscomment 02_Ship_At_Sea_(Instrumental).ogg’

If you use system, you can pass the arguments in an array, and the
shell
isn’t used, so you can have arguments that the shell would barf on.

system(“echo !@#$%^&”) == “”
system(“echo”, “!@#$%^&”) == “!@#$%^&”)

I need a backtick method that works the same way. Having popen work
that way would be nice. As it is, I think it’s only possible with
popen(“-”), having the child exec in such a way as to not interpolate
the arguments, and reading from that.
[snip]

I am not sure that I understand what you mean by “bypassing the shell”,

It’s simple. When you execute some command (or the equivalent
using %x), what Ruby actually executes is your command interpreter
(“shell” in Unix-speak), passing it the string between ...
to execute. It’s up to the shell to split up the command into
words, handle wildcard expansion (at least on UNIX; the command
interpreter on Windows leaves that latter duty up to the individual
commands), etc. The problem is that all this power is a security
hole because it’s easy to trick command interpreters into doing
ugly things just by passing something nefarious as a “filename”.

The original poster mentioned Kernel.system, which is the way to
execute a command when you don’t care about its output.
If you pass it one long string, Kernel.system will also invoke the
shell, but if instead you pass it separate arguments for each word of
the command line, it bypasses the shell and executes the command
directly. Thus:

system(“ps -fp#{$$}”) # also invokes the shell
system(‘ps’, “-fp#{$$}”) # doesn’t invoke the shell
psOutput = ps -fp#{$$} # invokes the shell

The question is: how do you complete the list, that is, capture the
command output without involving the shell?

The only solution of which I’m aware is to use popen/exec:

if fd = IO.popen(‘-’) then
psOutput = fd.readlines.join(“\n”)
else
exec ‘ps’, “-fp#{$$}”
end

Of course, you could turn this into a method:

def safeBackticks(*args)
if fd = IO.popen(‘-’) then
output = fd.readlines.join(“\n”)

	fd.close #right?
  else
  exec *args
  end
  return output

end

Thanks, that method is exactly what I’m looking for.

I only wonder why it isn’t in Ruby already. Although I personally
couldn’t care less, the above can only be done in Windows by calling
CreateProcess() directly. Using quotes only works if the filename doesn’t
contain quotes. Escaping it would work, but it’s a hack, and dependent on
the shell. (Windows’s cmd will expand %VAR%, IIRC.) It’s also kind of
weird to have a method whose only name is “`”.

ri doesn’t say what popen(“-”) does with no block, though I see
it returns twice like fork(). Sweet.

Have fun,

···

On Mon, 01 Sep 2003 00:21:11 +0000, Mark J. Reed wrote:

On Sun, Aug 31, 2003 at 06:00:34PM -0500, Shashank Date wrote:

Tom Felker, tcfelker@mtco.com
http://vlevel.sourceforge.net - Stop fiddling with the volume knob.

The ability to monopolize a market is insignificant next to the power of the
source.

I believe open3 will do what you want as well:

require ‘open3’
Open3.popen3(“cmd”,“args”,“go”,“here”,“etc”) { |in,out,err|
out.readlines.join(“\n”)
}

···

On Sunday 31 August 2003 6:26 pm, Mark J. Reed wrote:

On Sun, Aug 31, 2003 at 06:00:34PM -0500, Shashank Date wrote:

I am not sure that I understand what you mean by “bypassing the
shell”,

The only solution of which I’m aware is to use popen/exec:

if fd = IO.popen(‘-’) then
psOutput = fd.readlines.join(“\n”)
else
exec ‘ps’, “-fp#{$$}”
end


Wesley J. Landaker - wjl@icecavern.net
OpenPGP FP: 4135 2A3B 4726 ACC5 9094 0097 F0A9 8A4C 4CD6 E3D2

“Mark J. Reed” markjreed@mail.com wrote in message

···

On Sun, Aug 31, 2003 at 06:00:34PM -0500, Shashank Date wrote:

I am not sure that I understand what you mean by “bypassing the shell”,

It’s simple. When you execute some command (or the equivalent
using %x), what Ruby actually executes is your command interpreter
(“shell” in Unix-speak), passing it the string between ...
to execute. It’s up to the shell to split up the command into
words, handle wildcard expansion (at least on UNIX; the command
interpreter on Windows leaves that latter duty up to the individual
commands), etc.

[excellent explanation snipped]

Thanks a lot for sharing your insight. I have used Unix quite
a lot in the past, but somehow missed this basic understanding.

– shanko

“Austin Ziegler” austin@halostatue.ca wrote in message

I am not sure that I understand what you mean by “bypassing the shell”,
but will this work for you:

UNIX shells do globbing, whereas DOS (and CMD) leave that for the program
to
do the globbing.

Aha ! Thanks for the clarification.
– shanko

···

On Mon, 1 Sep 2003 08:06:30 +0900, Shashank Date wrote:

What you really want is a full shell-metacharacter escaper:

class String
def shellescape
self.dup.gsub!(/([&!#<>'" ])/) do |s|
# I think that’s all of them
‘\’ << s
end
end
end

and use vorbiscomment #{oldname.shellescape}

···

On Sun, 2003-08-31 at 17:51, Daniel Carrera wrote:

Try this:

  • vorbiscomment #{oldName}.each_line do |line|
  • vorbiscomment "#{oldName}".each_line do |line|

Thanks, that method is exactly what I’m looking for.

You’re welcome!

I only wonder why it isn’t in Ruby already.

I wouldn’t be surprised if it were, I just don’t know
where. In Perl, there’s a version of open that lets
you do the same thing, for instance.

It’s also kind of weird to have a method whose only name is “`”.

Why? Lots of methods only have operator names; Array#, for instance.

ri doesn’t say what popen(“-”) does with no block, though I see
it returns twice like fork(). Sweet.

Neither does the Pickaxe, but it a logical extrapolation from what’s
there, and besides, I tried it to see. :slight_smile: The return value if there’s
no block is the same as the parameter that is passed to the block
if there is one: the IO object representing the connection in the
parent process, nil in the child process.

-Mark

···

On Sun, Aug 31, 2003 at 08:14:35PM -0500, Tom Felker wrote: