`echo %!(*`

T

Tom Felker

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,
 
D

Daniel Carrera

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


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, <[email protected]>
<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.
 
S

Shashank Date

Tom Felker said:
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 ;)

HTH,
-- shanko
 
T

Tom Felker

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, <[email protected]>
<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(.9*i/w,.9*j/h);while n<=m&&(
z-c).abs<=2;z=z*z+c;n+=1 end;print [10+n*15,0,rand*99].pack("C*")}}'|display
#by Michael Neumann AFAICT
 
D

Daniel Carrera

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.

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, <[email protected]>
<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(.9*i/w,.9*j/h);while n<=m&&(
z-c).abs<=2;z=z*z+c;n+=1 end;print [10+n*15,0,rand*99].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.
 
T

Tom Felker

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,
--
Tom Felker, <[email protected]>
<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.
 
B

Brian Candler

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?

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.
 
J

Jason Creighton

I believe open3 will do what you want as well:

Yep, open3 will do it for him. It'd be nice if the "stock" popen would
support multiple arg to avoid the shell doing the splitting.
require 'open3'
Open3.popen3("cmd","args","go","here","etc") { |in,out,err|
^^
Syntax error here

'in' is a Ruby keyword (I think for the 'for x in y' construct.)

But other than that, the example should work.

Jason Creighton
 

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,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top