Извлечение нескольких шаблонов из строки независимо от порядка

Если вы пытаетесь подключить логический HDD/SDD

  • Двойная загрузка :Windows 10/Ubuntu
  • Я нашел это в поисках способа смонтировать мой диск Windows в Linux

Пройдено шагов

  • показать блочные устройства

    ℹ️ your HDD/SDD is a block storage device

    sudo blkid
    
    /dev/sda5: UUID="a6aa3891-1dc2-439a-b449-b9b1848db028" TYPE="ext4" PARTUUID="e4887e0f-05"
    /dev/sda1: LABEL="System" UUID="C6F4E92AF4E91E05" TYPE="ntfs" PARTUUID="e4887e0f-01"
    /dev/sda2: LABEL="Windows" UUID="4ABAF478BAF461BD" TYPE="ntfs" PARTUUID="e4887e0f-02"
    
  • В моем случаеЯ хочу смонтировать устройство с надписью «Windows»/dev/sda2

Что не получилось

  • Оказывается, я поменял местами аргументы команды mount, чтобы получить жалобу «не является блочным устройством»
    mkdir Windows
    sudo mount Windows /dev/sda2
    mount: /dev/sda2: /home/casey/Windows is not a block device.
    

Что сработало ‍♂️️

  • mountработает как босс, когда вы перечисляете аргументы в правильном порядке!
    sudo mount /dev/sda2 Windows 
    cd Windows 
    ls
    Config.Msi                hiberfil.sys   Intel         pagefile.sys   ProgramData     'Program Files (x86)'  '$Recycle.Bin'  'System Volume Information'   WCH.CN
    'Documents and Settings'   home           msdia80.dll   PerfLogs      'Program Files'   Recovery               swapfile.sys    Users                        Windows
    
3
20.08.2020, 15:39
5 ответов

С awk(, проверенным с GNU awk, не уверен, что он работает с другими реализациями)

$ cat kv.awk
/appID/ {
    for (i = 1; i <= NF; i++) {
        $i ~ /^port=/ && (a = $i)
        $i ~ /^authenticate=/ && (b = $i)
        $i ~ /^appID=/ && (c = $i)
    }
    print NR "\n" a, b, c
}

$ awk -v OFS='\t' -f kv.awk ip.txt
1
port=1234   authenticate=true   appID=dummyAppId1
2
port=1244   authenticate=false  appID=dummyAppId2
3
port=1235   authenticate=true   appID=dummyAppId3


Сperl

$ # note that the order is changed for second line here
$ cat ip.txt
process1 port=1234 authenticate=true appID=dummyAppId1 <some more params>
process3 port=1244 appID=dummyAppId2 authenticate=false <some more params>
process2 port=1235 authenticate=true appID=dummyAppId3 <some more params>

$ perl -lpe 's/(?=.*(port=[^ ]+))(?=.*(authenticate=[^ ]+))(?=.*(appID=[^ ]+)).*/$1\t$2\t$3/; print $.' ip.txt 
1
port=1234   authenticate=true   appID=dummyAppId1
2
port=1244   authenticate=false  appID=dummyAppId2
3
port=1235   authenticate=true   appID=dummyAppId3
  • (?=.*(port=[^ ]+))первая группа захвата дляport
  • (?=.*(authenticate=[^ ]+))вторая группа захвата для authenticateи так далее
  • print $.для номера строки
  • Чтобы избежать частичных совпадений, используйте \bport, \bappIDи т. д., если достаточно границы слова. В противном случае используйте (?<!\S)(port=[^ ]+)для ограничения на основе пробелов.

Если вам нужно напечатать только строки, содержащие appIDили любое другое подобное условие, измените -lpeна -lneи измените print $.на print "$.\n$_" if /appID/

.
8
18.03.2021, 23:11

С perlвы можете использовать такой подход, как:

perl -lne 'my %h;
           $h{$1} = $& while /(\S+?)=(\S+)/g;
           print "@h{qw(port authenticate appID)}"'

Где вы строите хеш-таблицу, ключи которой являются именами атрибутов, а значения — name=values, а затем печатаете те, которые вам нужны.

Замените $&на $2, если вам нужны только значения на выходе.

То же, что иawk:

awk '
  {
    split("", h)
    for (i = 1; i <= NF; i++)
      if (n = index($i, "=")) h[substr($i, 1, n - 1)] = $i
    print h["port"], h["authenticate"], h["appID"]
  }'

С помощью pcregrepвы можете сделать:

pcregrep -o1 -o2 -o3 --om-separator=' ' '(?x)
  ^(?=.*?\s(port=\S+))
   (?=.*?\s(authenticate=\S+))
   (?=.*?\s(appID=\S+))'

(что требуется присутствие всех трех атрибутов ).

Сsed:

sed 'G
     s/[[:space:]]\(port=[^[:space:]]*\).*\n.*/&\1/
     s/[[:space:]]\(authenticate=[^[:space:]]*\).*\n.*/& \1/
     s/[[:space:]]\(appID=[^[:space:]]*\).*\n.*/& \1/
     s/.*\n//'

Последние два предполагают, что атрибуты не являются первым словом строки (, что кажется разумным предположением, учитывая ваш образец ).

7
18.03.2021, 23:11

Если это может помочь другим пользователям с похожей проблемой, (подробное )предложение с использованием Ruby:

# passing the log file as parameter
lines = File.open(ARGV[0]).read.split("\n")

lines.each_with_index do |line, i|
  words  = line.split(' ')
  output = []

  puts i + 1
  output << words.select { |w| w =~ /port=\d+/ }
  output << words.select { |w| w =~ /authenticate=\w+/ }
  output << words.select { |w| w =~ /appID=\w+/ }

  puts output.join(' ')
end
0
18.03.2021, 23:11

Другие предоставили решения awkи perl.

В качестве отличия я предлагаю решение, использующее bashассоциативный массив:

#!/bin/bash

declare -A arr
lineno=0

while IFS=' =' read process key1 value1 key2 value2 key3 value3 restofline
do
   arr=( [$key1]="$value1" [$key2]="$value2" [$key3]="$value3" )
   printf "%d\nport=%s authenticate=%s appID=%s\n" \
          $((++lineno)) ${arr["port"]} ${arr["authenticate"]} ${arr["appID"]}
done < infile

который выводит:

$./demo
1
port=1234 authenticate=true appID=dummyAppId1
2
port=1244 authenticate=false appID=dummyAppId2
3
port=1235 authenticate=true appID=dummyAppId3
$

Это предполагает, что записи в файле расположены в соответствии с образцом, предоставленным OP в их вопросе.

Кто-то может возразить, что приведенный выше сценарий не является общим решением и, следовательно, в некотором роде является худшим решением. Я бы сказал, что не все решения проблемы должны быть общими. Действительно, часто простое точное решение лучше.

Вот более общее решение:

#!/bin/bash

declare arr1
declare -A arr2

while read -ra arr1; do
    for str in "${arr1[@]}"; do
        if [[ "$str" =~..*=..* ]]; then
            key=${str%=*}; val=${str#*=}; arr2[$key]="$val"
        fi
    done
    printf "port=%s authenticate=%s appID=%s\n" \
           ${arr2["port"]} ${arr2["authenticate"]} ${arr2["appID"]}
done < infile

который выводит:

$./demo
port=1234 authenticate=true appID=dummyAppId1
port=1244 authenticate=false appID=dummyAppId2
port=1235 authenticate=true appID=dummyAppId3
$
-1
18.03.2021, 23:11

Каждый раз, когда во входных данных есть пары имя=значение, я считаю, что лучше всего сначала создать массив, который содержит это сопоставление(f[])ниже, а затем вы можете получить доступ к значениям по их именам в любом порядке, который вам нравится, например.:

$ awk -F'[ =]' '{
    for (i=2;i<NF;i+=2) f[$i]=$i"="$(i+1)
    print f["port"], f["authenticate"], f["appID"]
}' file
port=1234 authenticate=true appID=dummyAppId1
port=1244 authenticate=false appID=dummyAppId2
port=1235 authenticate=true appID=dummyAppId3
1
18.03.2021, 23:11

Теги

Похожие вопросы