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.
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.
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.
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.
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?
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:
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.
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.
–
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.
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:
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.
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. 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: