【问题标题】:Ruby: execute shell command 'echo' with '-n' optionRuby:使用“-n”选项执行 shell 命令“echo”
【发布时间】:2011-06-30 20:25:07
【问题描述】:

我想从 Ruby 运行以下 shell 命令,它将一个字符串复制到剪贴板(在 OS X 上),'n' 正在抑制由echo 引起的字符串后的换行符:

echo -n foobar | pbcopy

—> 工作,很好,现在剪贴板包含“foobar”

我尝试了以下方法,但它们都总是将选项“-n”复制到剪贴板中:

%x[echo -n 'foobar' | pbcopy]
%x[echo -n foobar | pbcopy]
system "echo -n 'foobar' | pbcopy"
system "echo -n foobar | pbcopy"
exec 'echo -n "foobar" | pbcopy'
`echo -n "foobar" | pbcopy`
IO.popen "echo -n 'foobar' | pbcopy"

实现这一目标的正确方法是什么?

【问题讨论】:

    标签: ruby macos shell


    【解决方案1】:

    你的问题是-n只能被bash内置的echo命令理解;当您说%x[...](或您的任何其他变体)时,该命令将被馈送到/bin/sh,即使它确实是/bin/bash,它也会像POSIX shell一样。解决方案是将您的 shell 命令显式提供给 bash:

    %x[/bin/bash -c 'echo -n foobar' | pbcopy]
    

    当然,您在引用 foobar 的真实内容时需要小心。 -c 开关本质上是告诉 /bin/bash 你给它一个内联脚本:

    -c 字符串
    如果存在 -c 选项,则从 string 中读取命令。 如果字符串后面有参数,则将它们分配给位置 参数,以 $0 开头。

    【讨论】:

    • 谢谢!这看起来像我所追求的。我要复制的字符串是用户指定的密码,通常会进入密码字段,这就是为什么我也不能复制换行符的原因,因为它不一定会显示为这样,而是显示为额外的字符。您对此案还有其他建议吗?
    • @polarblau:如果您要输入密码,则使用popen 方法之一打开pbcopy 并通过标准输入发送密码可能更可靠,因为您不必这样做担心报价。锡人有一个使用标准输入和Open3.popen3()的例子。
    • %x[/bin/echo -n foobar | pbcopy] 会不会更容易些? (顺便说一句,%x[echo -n foobar | pbcopy] 对我来说似乎没问题。)
    • @matt:是的,%x[...] 的东西很有效,而且很简单。问题是“foobar”实际上可能是“ha'ha$PATH”或一些这样的混乱(毕竟它是一个密码)所以你必须确保所有内容都正确转义,直接打开pbcopy的管道避免那个问题。
    【解决方案2】:

    由于echo 在不同的shell 和/bin/echo 中的行为不同,建议您改用printf

    没有换行符:

    %x[printf '%s' 'foobar' | pbcopy]
    

    换行:

    %x[printf '%s\n' 'foobar' | pbcopy]
    

    【讨论】:

      【解决方案3】:

      您可能正在重新发明*。

      IRB_Tools 和 Utility_Belt 都用于调整 IRB,提供了使用剪贴板的能力。两者都是现有宝石的集合,所以我使用gem clipboard -r 进行了快速搜索并得出:

      clipboard (0.9.7)
      win32-clipboard (0.5.2)
      

      查看 clipboard 的 RubyDoc.info 会发现:

      剪贴板

      访问剪贴板,不管操作系统是 Linux、MacOS 还是 Windows。

      用法

      你有 Clipboard.copy,

      Clipboard.paste 和

      剪贴板.clear

      玩得开心;)


      编辑:如果您检查链接页面上的来源,对于 Mac,您将看到复制:

      def copy(data)
        Open3.popen3( 'pbcopy' ){ |input,_,_| input << data }
        paste
      end
      

      对于粘贴,您会看到:

      def paste(_ = nil)
        `pbpaste`
      end
      

      很简单:

      def clear
        copy ''
      end
      

      这些应该会让你指向正确的方向。

      【讨论】:

      • 谢谢!我可能会查看源代码,但对于我的目的而言,gem 可能有点矫枉过正。
      【解决方案4】:

      这可能看起来像一个丑陋的解决方法,但我很确定它会起作用:

      创建一个名为 myfoobar.sh 的可执行文件,其中包含您要执行的行。

      #! /bin/sh
      echo -n foobar | pbcopy
      

      然后从 ruby​​ 调用该文件。

      【讨论】:

      • 这仅在/bin/sh 是 bash(或其他一些其 echo 理解 -c 的 shell)并且配置为像 bash 而不是严格的 POSIX shell 时才有效。 /bin/sh 通常被配置为像严格的 POSIX shell 一样工作。如果你说#!/bin/bash,OTOH,那么你会得到更一致的结果echo -n
      • 谢谢,但在这种情况下我还是想避免额外的文件。
      【解决方案5】:

      使用 sutil 进行正确设置:

      $ ssh-copy-id 用户@主机

      【讨论】:

      最近更新 更多