根据第一行中出现的位置在 bash 中剪切字符串

May*_*y11 6 macos bash awk sed

我试图从命令输出中选择特定的列(2 和 3),其中列分隔符也出现在文本中。考虑这个示例文本(输出的虚构部分nmcli):

           SSID BSSID             RSSI CHANNEL
      Something 12:34:56:78:98:ab -10  1
Something guest a2:34:56:78:98:ab -10  2
        Network b2:34:56:78:98:ab -20  3 
 Public network c2:34:56:78:98:ab -30  4
Run Code Online (Sandbox Code Playgroud)

列由单个空格分隔,但 SSID 中也有单个空格。如果没有这些,使用cut.

在 GNU 中,我以某种方式使用 下划线替换 SSID 中的空格来sed使其工作:\w\s\w

sed -e 's/\w\s\w/_/g'
Run Code Online (Sandbox Code Playgroud)

但这在 Mac 上不起作用。所以我的第二个最好的想法是按职位划分。是否有可能:

  1. 确定第一行中 BSSID、RSSI 和 CHANNEL 字符串的位置
  2. 在 BSSID_pos 和 RSSI_pos (1) 以及 RSSI_pos - CHANNEL (2) 之间切换

有更聪明的方法吗?SSID 中的那些空格也会让 awk 感到困惑,所以

awk '{print $2 " " $3}'
Run Code Online (Sandbox Code Playgroud)

在这些行中不起作用。

--- 编辑 --- 问题是,当使用 MacOS airport -s 生成的数据时,最后一个字段也有空格,所以我也不能从后面走:

                            SSID BSSID             RSSI CHANNEL HT CC SECURITY (auth/unicast/group)
                             AA1 d8:12:b6:b8:10:d5 -92  9       Y -- WPA(PSK/AES/AES) RSN(PSK/AES/AES)
                     XXXXX Guest ba:11:e4:4a:01:71 -90  6       Y -- RSN(PSK/AES/AES)
                         Netwrrk 5a:99:68:b7:0d:ca -89  36      Y DE RSN(PSK/AES/AES)
                      T-3_7edb87 64:11:ea:7e:db:87 -87  11      Y -- WPA(PSK/AES,TKIP/TKIP) RSN(PSK/AES,TKIP/TKIP)
                           abcde 58:22:8f:c6:7c:de -86  6       Y -- RSN(PSK/AES/AES)
                           abcde 64:66:88:3f:b2:9a -78  8       Y -- RSN(PSK/AES,TKIP/TKIP)
                           ababa 74:ac:77:b1:e3:59 -74  6       Y -- RSN(PSK/AES/AES)

Run Code Online (Sandbox Code Playgroud)

对两个问题合二为一表示歉意;谢谢你们!

Ren*_*let 5

以下内容已使用 GNU和macOS 附带的awkBSD进行了测试(但正如评论所述,它应该适用于任何 POSIX ):awkEdawk

$ awk 'NR==1 {n = match($0, /[[:space:]]BSSID[[:space:]]/) + 1}
       NR>1  {$0 = substr($0, n); print $1, $2}' file.txt
12:34:56:78:98:ab -10
a2:34:56:78:98:ab -10
b2:34:56:78:98:ab -20
c2:34:56:78:98:ab -30 
Run Code Online (Sandbox Code Playgroud)

BSSID第一个块仅适用于第一行并存储in 变量的索引n。第二块适用于所有其他行。它通过抑制n-1第一个字符来修改它们,并打印新的第一个和第二个字段。

只是为了好玩(因为它有点复杂),这里还有一个sed基于解析标题行的相同想法的解决方案,并使用 GNU和macOS 附带的sedBSD进行了测试。sed

$ sed -En '
1 {
  s/^(.*[[:space:]])BSSID[[:space:]].*/\1/
  h
}
2,$ {
  G
  :a
  s/.(.*\n.*)./\1/
  ta
  s/^([^[:space:]]+[[:space:]]+[^[:space:]]+).*/\1/
  p
}' file.txt
12:34:56:78:98:ab -10
a2:34:56:78:98:ab -10
b2:34:56:78:98:ab -20
c2:34:56:78:98:ab -30
Run Code Online (Sandbox Code Playgroud)

我们使用扩展正则表达式 ( -E) 并抑制默认回显 ( -n)。

BSSID我们首先删除从(替换命令)开始的第一行的末尾s/^(.*[[:space:]])BSSID[[:space:]].*/\1/并将结果存储在保留空间(h)中。在第一个示例中,保留空间现在包含(开始和结束标记为^and $):

^           SSID $
Run Code Online (Sandbox Code Playgroud)

除第一行 ( 2,$) 之外的所有行均按如下方式修改并打印(最终p):

  • 添加换行符,后跟保留空格 ( G)。示例的第二行变为:

    ^      Something 12:34:56:78:98:ab -10  1\n           SSID $
    
    Run Code Online (Sandbox Code Playgroud)
  • 通过删除换行符后的第一个字符和最后一个字符来迭代(循环:a... ta),直到删除换行符后的所有字符(替换命令s/.(.*\n.*)./\1/)。示例的第二行变为:

    ^12:34:56:78:98:ab -10  1\n$
    
    Run Code Online (Sandbox Code Playgroud)
  • 删除第二个空格字符串 ( ) 之后(包括)的所有内容s/^([^[:space:]]+[[:space:]]+[^[:space:]]+).*/\1/。示例的第二行变为:

    ^12:34:56:78:98:ab -10$
    
    Run Code Online (Sandbox Code Playgroud)

如果重要的话, GNUsed版本更紧凑一些:

^           SSID $
Run Code Online (Sandbox Code Playgroud)


Ed *_*ton 5

在每个 Unix 机器上的任何 shell 中使用任何 awk:

$ awk 'NR==1{ match($0,/BSSID.*RSSI/); next } { $0=substr($0,RSTART,RLENGTH); $1=$1; print }' file
d8:12:b6:b8:10:d5 -92
ba:11:e4:4a:01:71 -90
5a:99:68:b7:0d:ca -89
64:11:ea:7e:db:87 -87
58:22:8f:c6:7c:de -86
64:66:88:3f:b2:9a -78
74:ac:77:b1:e3:59 -74
Run Code Online (Sandbox Code Playgroud)


Rav*_*h13 4

这里使用 GNU 怎么样grep(在 RedHat 中测试),它将通过使用正则表达式来简单地捕获所需的字符串。仅使用所示示例进行编写和测试。

这是使用正则表达式的在线演示。

grep -oP '(?<=\s)[0-9a-f]{2}(:[0-9a-f]{2}){5}\s+-?[[:digit:]]{2}'  Input_file
Run Code Online (Sandbox Code Playgroud)

或者尝试perl以下(感谢@tripleee):

perl -nle 'm/(?<=\s)[0-9a-f]{2}(:[0-9a-f]{2}){5}\s+-?[[:digit:]]{2}/ and print "$&"'  Input_file
Run Code Online (Sandbox Code Playgroud)

  • MAC 仅包含用冒号分隔的十六进制字符,但是,当然应该是“[0-9a-f]”而不是“[0-9a-f}” (2认同)
  • 您可以使用 `perl -nle 'm/(?&lt;=\s)[0-9a-f]{2}(:[0-9a-f]{2}){5}\s+-?[ 来近似它[:digit:]]{2}/ 并打印“$&amp;”'` (2认同)