bash中引用参数的行列表

时间:2018-04-09 04:55:41

标签: bash

我有一个命令,它返回由引号括起的网络接口列表: -

networksetup -listnetworkserviceorder | grep "Hardware Port" | grep -v "Wi-Fi" | sed 's#([^:]*:\ \([^,]*\),.*#\"\1\"#'

现在我需要将其转换为另一个命令的参数: -

networksetup -ordernetworkservices "Wi-Fi" <the other interfaces>.

我试过

services=`networksetup -listnetworkserviceorder | grep "Hardware Port" | grep -v "Wi-Fi" | sed 's#([^:]*:\ \([^,]*\),.*#\"\1\"#' | paste -sd " " -`
# set order after above
networksetup -ordernetworkservices Wi-Fi $services

但那是失败的。似乎我的报价被剥夺了,但我无法弄清楚原因。

echo $services 

给了我

"Dell Universal Dock D6000" "USB 10/100/1000 LAN" "Bluetooth PAN" "Thunderbolt Bridge"

我还尝试使用单引号和双引号包围的变量进行上述操作。

该命令将吐出到shell:

"Dell Universal Dock D6000"
"USB 10/100/1000 LAN"
"Bluetooth PAN"
"Thunderbolt Bridge"

我最终需要的是:

networksetup -ordernetworkservices Wi-Fi "Dell Universal Dock D600" "USB 10/100/1000 LAN" "Bluetooth PAN" "Thunderbolt Bridge"

我知道答案就在我面前,但是我错过了。

3 个答案:

答案 0 :(得分:2)

如果没有这样的网络设置程序,很难说清楚。但是看看:

mock () {
  echo '"Dell Universal Dock D6000"
"USB 10/100/1000 LAN"
"Bluetooth PAN"
"Thunderbolt Bridge"'
}

mock
"Dell Universal Dock D6000"
"USB 10/100/1000 LAN"
"Bluetooth PAN"
"Thunderbolt Bridge"

echo $(mock)
"Dell Universal Dock D6000" "USB 10/100/1000 LAN" "Bluetooth PAN" "Thunderbolt Bridge"

echo "$(mock)"
"Dell Universal Dock D6000"
"USB 10/100/1000 LAN"
"Bluetooth PAN"
"Thunderbolt Bridge"

for s in $(echo "$(mock)"); do echo "<"$s">"; done 
<"Dell>
<Universal>
<Dock>
<D6000">
<"USB>
<10/100/1000>
<LAN">
<"Bluetooth>
<PAN">
<"Thunderbolt>
<Bridge">

for s in "$(echo "$(mock)")"; do echo "<"$s">"; done 
<"Dell Universal Dock D6000" "USB 10/100/1000 LAN" "Bluetooth PAN" "Thunderbolt Bridge">

答案 1 :(得分:2)

首先:在shell中,您应该将引号视为围绕数据的内容,而不是数据中的内容。您在数据中嵌入了引号,并期望它们像数据一样工作,并且它不会以这种方式工作。更具体地说,shell在替换变量值之前解析引号(以及转义和其他一些东西),所以如果你在变量的值中有引号,那么当它被替换到命令行时它会&# 39;为了达到预期的效果为时已晚。

那么,如果每个端口名称可能包含空格,您将如何将其保存为单独的项目?在bash中,您可以将它们存储在一个数组中,并使用"${arrayname[@]}"扩展数组 - 双引号和[@]的组合使bash将每个数组元素视为一个单独的单词,基本上就像每个项目被单独引用。

那么如何将networksetup的输出转换为数组呢?好吧,如果你有bash版本4,你可以使用readarray,但macOS只附带bash v3。你仍然可以做到这一点,它只是一点点工作。您有一个read循环读取networksetup的每一行输出,经过处理以删除除服务名称之外的所有内容(并且不添加引号)。但是你不能让循环成为管道的一部分,或者它将在子shell中运行,并且数据在退出时会丢失,因此你必须重定向循环的输入。命令用< <( )替换。像这样:

service_list=()
while read service; do
    service_list+=("$service")    # Note: double-quotes and parens are both needed
done < <(networksetup -listnetworkserviceorder | grep "Hardware Port" | grep -v "Wi-Fi" | sed 's#([^:]*:\ \([^,]*\),.*#\1#')

networksetup -ordernetworkservices Wi-Fi "${service_list[@]}"

BTW,对于@userunknown和其他无法访问macOS试图解决/测试这个问题的人来说,这是一个示例输出:

$ networksetup -listnetworkserviceorder
An asterisk (*) denotes that a network service is disabled.
(1) iPhone USB
(Hardware Port: iPhone USB, Device: en5)

(2) Ethernet
(Hardware Port: Ethernet, Device: en0)

(3) Wi-Fi
(Hardware Port: Wi-Fi, Device: en1)

(*) FireWire
(Hardware Port: FireWire, Device: fw0)

(4) Bluetooth PAN
(Hardware Port: Bluetooth PAN, Device: en4)

(5) Thunderbolt Bridge
(Hardware Port: Thunderbolt Bridge, Device: bridge0)

答案 2 :(得分:1)

您似乎想重新排序网络端口,因此Wi-Fi位于其他所有位置之前。

您尝试创建命令,就像在命令行上键入命令一样。虽然,这是一个将参数放在一个顺序中的问题。所以不要在网络名称周围加上引号,只要让参数代表它们自己,例如。

networksetup -listnetworkserviceorder | grep "Hardware Port" | grep -v "Wi-Fi" |
    sed 's#([^:]*:\ \([^,]*\),.*#\1#'

现在您可以将它们与xargs

放在一起
networksetup -listnetworkserviceorder | grep "Hardware Port" | grep -v "Wi-Fi" |
    sed 's#([^:]*:\ \([^,]*\),.*#\1#' |
    xargs networksetup -ordernetworkservices Wi-Fi

但是这并没有像预期的那样工作,因为硬件端口内部有空格(因此需要引用)。要解决此问题,您可以将硬件端口分隔为NUL字节而不是换行符。要用NUL字节替换换行符,请使用tr

networksetup -listnetworkserviceorder | grep "Hardware Port" | grep -v "Wi-Fi" |
    sed 's#([^:]*:\ \([^,]*\),.*#\1#' | tr -s '\n' '\0' |
    xargs -0 networksetup -ordernetworkservices Wi-Fi

现在xargs正确地将参数传递给networksetup。