You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Elixir的System.cmd中执行带管道的USB设备查询命令?

在Elixir的System.cmd中执行带管道的命令

你遇到的问题其实是因为System.cmd和:os.cmd的工作方式不一样::os.cmd会默认通过系统shell来解析命令(包括管道、重定向这些shell语法),但System.cmd是直接调用指定的可执行文件,不会处理shell的特殊符号——所以你传的["|", "grep", ...]会被当成usb-devices的命令行参数,而不是用来触发管道的,自然就执行失败了。

下面给你两种解决办法,按需选择:

方法一:通过shell来执行整个命令

既然管道是shell的功能,那我们可以把整个命令字符串交给shell去执行。在类Unix系统中,通常用bash -c "你的命令"的方式:

case System.cmd("bash", ["-c", "usb-devices | grep -A 1 Product="]) do
  {output, 0} ->
    # 命令执行成功,处理输出
    IO.puts("已连接的USB设备信息:\n#{output}")
  {error_msg, exit_code} ->
    # 命令执行失败,处理错误
    IO.puts("获取设备信息失败,退出码:#{exit_code},错误信息:#{error_msg}")
end

这种方法的好处是完全复用你原来的shell命令,不需要改逻辑,和:os.cmd的行为最接近;缺点是依赖系统的shell(比如bash),如果你的代码要跨不同系统(比如Windows),可能需要调整。

方法二:用Elixir自己处理输出(更推荐)

为了避免依赖shell,我们可以先单独执行usb-devices获取所有输出,再用Elixir的字符串/正则工具过滤出需要的内容。这样更符合Elixir的函数式风格,也更跨平台:

case System.cmd("usb-devices", []) do
  {full_output, 0} ->
    # 过滤出Product=开头的行,以及每行后面紧跟的一行(对应grep -A 1的效果)
    filtered_lines =
      full_output
      |> String.split("\n")
      |> Enum.reduce([], fn line, acc ->
        if String.starts_with?(line, "Product=") do
          # 找到当前行的索引,把当前行和下一行加入结果
          index = Enum.find_index(String.split(full_output, "\n"), &(&1 == line))
          next_line = Enum.at(String.split(full_output, "\n"), index + 1, "")
          acc ++ [line, next_line]
        else
          acc
        end
      end)
      |> Enum.reject(&(&1 == "")) # 去掉空行

    IO.puts("已连接的USB设备信息:\n#{Enum.join(filtered_lines, "\n")}")

  {error_msg, exit_code} ->
    IO.puts("执行usb-devices失败,退出码:#{exit_code},错误信息:#{error_msg}")
end

或者用正则表达式更简洁地匹配:

case System.cmd("usb-devices", []) do
  {full_output, 0} ->
    # 匹配Product=开头的行,以及后面的一行
    matches = Regex.scan(~r/Product=.*?(?:\n.*?)?/, full_output)
    IO.puts("已连接的USB设备信息:\n#{Enum.join(List.flatten(matches), "\n")}")

  {error_msg, exit_code} ->
    IO.puts("执行usb-devices失败,退出码:#{exit_code},错误信息:#{error_msg}")
end

这种方法的好处是不依赖shell,代码更可控,跨平台性更好;缺点是需要自己实现grep的过滤逻辑,不过对于简单的场景来说完全没问题。

为什么System.cmd不处理管道?

System.cmd的设计目标是直接调用可执行文件,避免shell注入的风险,同时让命令执行的行为更明确。如果需要shell的特性,就显式通过bash -c来调用,这样开发者能清楚知道自己在依赖shell的功能。

内容的提问来源于stack exchange,提问作者D.R

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:38:45