Domanda

irb(main):001:0> require 'benchmark'
=> true
irb(main):002:0> puts Benchmark.measure { system "xclip .bashrc" }
  0.000000   0.000000   0.000000 (  0.008030)
=> nil
irb(main):003:0> puts Benchmark.measure { `xclip .bashrc` }
  0.000000   0.000000   0.000000 ( 33.215158)
=> nil
irb(main):004:0> RUBY_VERSION
=> "2.0.0"

I have read alomost everything on the internet about various ways to call shell command from ruby script, but I just can't figure out why Kernel#` takes much much longer than Kernel#system.

Update:

Kernel#` is much slower only with xclip. All the other commands takes almost the same time.

È stato utile?

Soluzione

I doubt that xclip just takes a long time to terminate when you use backticks to shell out. This has to do with the selection. Without any selection provided through -sel it will default to XA_PRIMARY which is conventionally used to implement copying and pasting via the middle mouse button.

When you run

$ xclip text.txt

the content becomes available through XA_PRIMARY which means you can paste it through your middle mouse button or $ xclip -o. It starts to get weird when you execute it thorugh a shell out in Ruby:

ruby -e '`xclip text.txt`

It never really terminates if you do nothing. It terminates when you select someting in your X11 system, for instance in the console or everywhere else. Just selection, marking something with your mouse. If you do not, it will hang and/or timeout at some point.

The same behavior can be observed when you use the verbose mode:

$ xclip -verbose text.txt

Connected to X server.
Using UTF8_STRING.
Reading text.txt...
Waiting for selection requests, Control-C to quit
  Waiting for selection request number 1

The selection request, again, is served when you select something.

A good analysis tool for this is strace (the -f option is for tracking forks, too)

$ strace -f ruby -e '`xclip text.txt`'

...
poll([{fd=3, events=POLLIN|POLLOUT}], 1, 4294967295) = 1 ([{fd=3, revents=POLLOUT}])
writev(3, [{"\20\0\3\0\4\0\200\2INCR", 12}, {NULL, 0}, {"", 0}], 3) = 12
poll([{fd=3, events=POLLIN}], 1, 4294967295) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "\1\0\f\0\0\0\0\0\235\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 4096,                                                                      0, NULL, NULL) = 32
recvfrom(3, 0x165e964, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
recvfrom(3, 0x165e964, 4096, 0, 0, 0)   = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=3, events=POLLIN}], 1, 4294967295

In the last line it hangs until a selection is made. poll() is used to wait for a file event at the file descriptor. It will terminate at some point, but 4,294,967,295 ms is rather long. It can be traced the same with with just strace -f xclip text.txt.

You can take a look at the file descriptors through ls -l /proc/PID/fd. The one with number 3 is the file descriptor where xclip waits for your selection.

What makes it so hard to debug is that if terminates instantly with strace xclip text.txt, but not with strace -f text.txt. The moment where you want to trace the fork it does not work anymore. That is the same problem you have with Ruby. It tries to trace the output, because Kernel#` wants to return the output. This has probably also to do with the ticket #9 Not closing stdout when setting clipboard from stdin.

This is my theory. The moment you shell out and want to follow the output of xclip, be it with Ruby to read from standard out, or strace to trace the forks, the standard output does not get closed until you make a selection.

This does not explain it very well, but it demonstrates that is does not have to do anything with Ruby. I will create a question which focuses on xclip only and not in the context of Ruby.

Altri suggerimenti

You need to provide more specifics, about what you are doing with xclip. B/c (because) my ruby 1.9.3 using "echo hello" shows minimal difference.

$ irb
irb(main):001:0> require 'benchmark'
=> true
irb(main):002:0> puts Benchmark.measure {  `echo hello` }
  0.000000   0.000000   0.000000 (  0.001471)
=> nil
irb(main):003:0> puts Benchmark.measure { system "echo hello" }
hello
  0.000000   0.000000   0.000000 (  0.001598)
=> nil
irb(main):004:0> RUBY_VERSION
=> "1.9.3"
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top