Estável ordenar um arquivo pela presença de substrings ordenadas em cada linha

4

Eu tenho uma lista de fontes de som que estou processando com um script. Um exemplo seria:

alsa_input.usb-AVEO_Technology_Corp._USB2.0_Camera-02-Camera.analog-mono
alsa_input.pci-0000_00_14.2.analog-stereo
alsa_input.usb-Plantronics_Plantronics_GameCom_780-00-P780.analog-stereo

Eu gostaria de classificá-los por substrings que estão em posições arbitrárias.

Por exemplo, eu gostaria que sort --by usb file.txt colocasse os dispositivos USB primeiro (preservando a ordem deles):

alsa_input.usb-AVEO_Technology_Corp._USB2.0_Camera-02-Camera.analog-mono
alsa_input.usb-Plantronics_Plantronics_GameCom_780-00-P780.analog-stereo
alsa_input.pci-0000_00_14.2.analog-stereo

Eu gostaria de poder especificar várias substrings para obter uma prioridade de granularidade mais precisa. Portanto, sort --by Platronics --by usb file.txt colocaria qualquer linha contendo "Platronics" primeiro, seguida pelas linhas contendo "usb", seguidas pelas demais linhas.

Posso fazer isso com qualquer tipo de utilitário de linha de comando?

    
por Stephen Ostermiller 22.05.2015 / 19:09

4 respostas

5

Parece que você quer um sistema de pontuação. Escreva um script para atribuir uma pontuação a cada linha, indicando quão cedo a saída você quer ver. awk parece bem adequado para este trabalho. Por exemplo:

#!/bin/sh
awk '{score=0}
    /usb/ {score=1}
    /Plantronics/ {score=2}
    {print score, NR, $0}' "$@"

Isso atribui uma pontuação de 0 a cada linha por padrão e, em seguida, substitui-o por um 1 se a linha contiver usb e 2 se a linha contiver Plantronics . Coloquei as instruções usb e Plantronics nessa ordem, se uma linha contiver ambas as strings, o valor final será 2. Então

./score file.txt | sort -k1nr -k2n | cut -d" " -f3-

(onde score é o nome do script). sort -k1nr significa classificar com base no primeiro campo (a pontuação), tratando-o como um número e classificando os valores mais altos primeiro (porque o script score atribuiu pontuações altas às linhas você está mais interessado em). -k2n significa, para linhas que possuem o mesmo valor no primeiro campo, classificar pelo segundo campo, como um número em ordem normal e crescente. O segundo campo é NR , o número do registro (a.k.a. line number). Isso garante que as linhas com a mesma pontuação (por exemplo, aqueles que contêm usb , mas não Plantronics ) sai em sua ordem original. Se você não se importa com isso, exclua a declaração NR, da print , exclua o -k2n do comando sort e altere o -f3- para -f2- . (Na verdade, sort pode preservar a ordem assim por padrão, então você pode não precisar disso.) Claro que o cut -d" " -f3- retira os números que o script score foi adicionado aos dados.

Se você não entender como isso funciona, tente executar

./score file.txt

e

./score file.txt | sort -k1nr -k2n

Essa abordagem é bastante flexível. Por exemplo, o código acima produzirá, em ordem,

  • todas as linhas contendo Plantronics ,
  • todas as linhas contendo usb (mas não Plantronics ) e
  • todas as linhas que não contêm nenhum dos itens acima

com cada grupo classificado em ordem de aparição no arquivo de entrada. Mas, alterando o script score da seguinte maneira,

#!/bin/sh
awk '{score=0}
    /usb/ {score+=1}
    /Plantronics/ {score+=2}
    {print score, NR, $0}' "$@"

podemos atribuir uma pontuação de 3 a linhas que contenham ambas as strings, então agora temos

  • todas as linhas que contêm Plantronics e usb , seguidas por
  • todas as linhas que contêm Plantronics (mas não usb ), seguidas por
  • todas as linhas que contêm usb (mas não Plantronics ) e, em seguida,
  • todas as linhas que não contêm nenhuma das opções acima.
por 22.05.2015 / 21:47
2

Com a cláusula sqlite e ORDER BY :

$sqlite3 <<\EOT
CREATE TABLE file(line);
.import file.txt file
SELECT * FROM file
ORDER BY
CASE
    WHEN line LIKE '%USB%' THEN 0
    WHEN line LIKE '%Realtek%' THEN 1
    ELSE 3
END;
EOT
00:10.0 USB controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 Controller (rev 81)
00:10.1 USB controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 Controller (rev 81)
00:10.2 USB controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 Controller (rev 81)
00:10.3 USB controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 Controller (rev 81)
00:10.4 USB controller: VIA Technologies, Inc. USB 2.0 (rev 86)
00:05.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (rev 10)
00:06.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (rev 10)
00:07.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (rev 10)
00:00.0 Host bridge: VIA Technologies, Inc. CN700/VN800/P4M800CE/Pro Host Bridge
00:00.1 Host bridge: VIA Technologies, Inc. CN700/VN800/P4M800CE/Pro Host Bridge
00:00.2 Host bridge: VIA Technologies, Inc. CN700/VN800/P4M800CE/Pro Host Bridge
00:00.3 Host bridge: VIA Technologies, Inc. PT890 Host Bridge
00:00.4 Host bridge: VIA Technologies, Inc. CN700/VN800/P4M800CE/Pro Host Bridge
00:00.7 Host bridge: VIA Technologies, Inc. CN700/VN800/P4M800CE/Pro Host Bridge
00:01.0 PCI bridge: VIA Technologies, Inc. VT8237/VX700 PCI Bridge
00:0f.0 IDE interface: VIA Technologies, Inc. VIA VT6420 SATA RAID Controller (rev 80)
00:0f.1 IDE interface: VIA Technologies, Inc. VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06)
00:11.0 ISA bridge: VIA Technologies, Inc. VT8237 ISA bridge [KT600/K8T800/K8T890 South]
01:00.0 VGA compatible controller: VIA Technologies, Inc. CN700/P4M800 Pro/P4M800 CE/VN800 Graphics [S3 UniChrome Pro] (rev 01)
    
por 22.05.2015 / 21:54
2

O que você está fazendo não está realmente classificando, mas selecionando. Então, o que você gostaria de fazer é primeiro selecionar todas as linhas que contêm "Platronics", depois todas as linhas que contêm "usb" e assim por diante e, finalmente, todas as linhas que não correspondem a nada.

Eu não sei de um comando que faça isso de uma só vez, mas você pode escrever um pequeno script usando python que faz o que você quer:

import sys

substrings = sys.argv[1:]
lines = sys.stdin.readlines()

# for each substring, print matching lines
for substring in substrings:
    i=0
    while i<len(lines):
        if substring in lines[i]:
            # match: print and remove
            sys.stdout.write(lines[i])
            del lines[i]
        else:
            # no match: try next line
            i += 1

# finally, print all lines that weren't matched
for line in lines:
    sys.stdout.write(line)

Ele faz o que você quer:

$ lspci | python substrings.py "USB controller" "PCI bridge"
00:1a.0 USB controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #4 (rev 02)
00:1a.1 USB controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #5 (rev 02)
00:1a.2 USB controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #6 (rev 02)
00:1a.7 USB controller: Intel Corporation 82801I (ICH9 Family) USB2 EHCI Controller #2 (rev 02)
00:1d.0 USB controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #1 (rev 02)
00:1d.1 USB controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #2 (rev 02)
00:1d.2 USB controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #3 (rev 02)
00:1d.7 USB controller: Intel Corporation 82801I (ICH9 Family) USB2 EHCI Controller #1 (rev 02)
00:1c.0 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 1 (rev 02)
00:1c.1 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 2 (rev 02)
00:1e.0 PCI bridge: Intel Corporation 82801 PCI Bridge (rev 92)
00:00.0 Host bridge: Intel Corporation 82Q33 Express DRAM Controller (rev 02)
00:02.0 VGA compatible controller: Intel Corporation 82Q33 Express Integrated Graphics Controller (rev 02)
00:02.1 Display controller: Intel Corporation 82Q33 Express Integrated Graphics Controller (rev 02)
00:03.0 Communication controller: Intel Corporation 82Q33 Express MEI Controller (rev 02)
00:19.0 Ethernet controller: Intel Corporation 82566DM-2 Gigabit Network Connection (rev 02)
00:1b.0 Audio device: Intel Corporation 82801I (ICH9 Family) HD Audio Controller (rev 02)
00:1f.0 ISA bridge: Intel Corporation 82801IB (ICH9) LPC Interface Controller (rev 02)
00:1f.2 IDE interface: Intel Corporation 82801IB (ICH9) 2 port SATA Controller [IDE mode] (rev 02)
00:1f.5 IDE interface: Intel Corporation 82801I (ICH9 Family) 2 port SATA Controller [IDE mode] (rev 02)
    
por 22.05.2015 / 20:16
1

arquivo cat:

22
z
there's
a
moose
loose
in
the
hoose

por exemplo. Definir argumentos para: loose moose - lidos pelo script como "$@" Você pode usar quantas quiser.

by=( "$@" )
nl file | sed -nf <(for ((i=0;i<${#by[@]};i++)) ;do 
                      echo "s/.*${by[i]}.*/$i\t&/; t p"
                    done; echo "s/^/$i\t&/; :p p") |
            sort -nk1 -nk2 | cut -f3-

Saída:

loose
moose
22
z
there's
a
in
the
hoose
    
por 22.05.2015 / 22:40