синтаксический анализ bash с несколькими условиями - PullRequest
1 голос
/ 05 октября 2019

У меня есть следующий код, который проверяет строки на 10 слов и разбивает их там, где появляется первый символ запятой. Он повторяет процесс, поэтому все вновь разделенные строки с более чем 10 словами и запятыми также разделяются (в конце концов, нет строк с более чем 10 словами и запятыми).

Как мне отредактировать этот код, чтобы сделать следующее: после того, как все разделение запятых выполнено (что уже делает текущий код), результирующие строки проверяются, если они имеют более 10 слов, и разделяются, где первый "а "(с пробелом) появляется?

#!/usr/bin/env bash

input=input.txt
temp=$(mktemp ${input}.XXXX)
trap "rm -f $temp" 0

while awk '
  BEGIN { retval=1 }
  NF >= 10 && /, / {
    sub(/, /, ","ORS)
    retval=0
  }
  1
  END { exit retval }
' "$input" > "$temp"; do
  mv -v $temp $input
done

Пример ввода:

Word1 Word2 Word3 Word4, Word5 Word6 Word7 Word8 Word9

Word1 Word2 Word3 Word4, Word5 Word6 Word7 Word8 Word9 Word10 Word11

Word1 Word2 Word3 Word4, Word5 Word6 Word7 Word8 Word9 Word10, Word11 Word12 Word13 Word14 Word15 Word16 

Word1 Word2 Word3 Word4, Word5 Word6 Word7 Word8 Word9 Word10 Word11 and Word12 Word13 Word14 Word15 

Word1 Word2 Word3 Word4 and Word5

Желаемый вывод:

Word1 Word2 Word3 Word4, Word5 Word6 Word7 Word8 Word9

Word1 Word2 Word3 Word4, 
Word5 Word6 Word7 Word8 Word9 Word10 Word11

Word1 Word2 Word3 Word4,
 Word5 Word6 Word7 Word8 Word9 Word10,
 Word11 Word12 Word13 Word14 Word15 Word16 

Word1 Word2 Word3 Word4, 
Word5 Word6 Word7 Word8 Word9 Word10 Word11 and
 Word12 Word13 Word14 Word15 

Word1 Word2 Word3 Word4 and Word5

Заранее спасибо!

Ответы [ 2 ]

1 голос
/ 07 октября 2019

Пожалуйста, попробуйте следующее:

awk '{
    while (split($0, a, "( +and +)|( +)") > 10 && match($0, "( +and +)|,")) {
        if (match($0, "[^,]+,")) {
            # puts a newline after the 1st comma
            print substr($0, 1, RLENGTH)
            $0 = substr($0, RLENGTH + 1)
        } else {
            # puts a newline before the 1st substring " and "
            n = split($0, a, " +and +")
            if (a[1] == "") {               # $0 starts with " and "
                a[1] = " and " a[2]
                for (i = 2; i < n; i++) {
                    a[i] = a[i+1]
                }
                n--
            }
            print a[1]
            $0 = " and " a[2]
            for (i = 3; i <= n; i++) {      # there are two ore more " and "
                $0 = $0 " and " a[i]
            }
        }
    }
    print
}' input.txt

Вывод для данного ввода:

Word1 Word2 Word3 Word4, Word5 Word6 Word7 Word8 Word9

Word1 Word2 Word3 Word4,
 Word5 Word6 Word7 Word8 Word9 Word10 Word11

Word1 Word2 Word3 Word4,
 Word5 Word6 Word7 Word8 Word9 Word10,
 Word11 Word12 Word13 Word14 Word15 Word16

Word1 Word2 Word3 Word4,
 Word5 Word6 Word7 Word8 Word9 Word10 Word11
 and Word12 Word13 Word14 Word15

Word1 Word2 Word3 Word4 and Word5

[Пояснения]

  • Он выполняет итерацию для одной и той же записи, в то время как пространство шаблона содержит более 10 полей (исключая слово «и») && пространство шаблона включает разделитель (и) строк для включения последовательного разделения.
  • Если шаблонпробел содержит запятую, затем напечатайте левую руку и обновите пробел правой рукой.
  • Если пробел содержит слово «и», обработка немного затруднена, поскольку слово остается в обновленномобразец пространства. Мой подход может быть не совсем элегантным в некотором смысле, но он работает, даже если запись содержит несколько (два или более) »и« s.

[EDIT]

Если вы хотите включить слово and в число слов, замените вторую строку:

while (split($0, a, "( +and +)|( +)") > 10 && match($0, "( +and +)|,")) {

на:

while (NF > 10 && match($0, "( +and +)|,")) {

Кроме того, еслиВы разрешаете слову and следовать за исходной строкой: сценарий будет немного упрощен как:

awk '{
    while (NF > 10 && match($0, "( +and +)|,")) {
        if (match($0, "[^,]+,")) {
            # puts a newline after the 1st comma
            print substr($0, 1, RLENGTH)
            $0 = substr($0, RLENGTH + 1)
        } else {
            # puts a newline after the 1st substring " and "
            n = split($0, a, " +and +")
            print a[1] " and"
            $0 = " " a[2]
            for (i = 3; i <= n; i++) {      # there are two ore more " and "
                $0 = $0 " and " a[i]
            }
        }
    }
    print
}' input.txt

Более того, если Perl является вашим вариантом, вы можете сказать:

perl -ne '{
    while (split > 10 && /( +and +)|,/) {
        if (/^.*?(, *| +and +)/) {
            print $&, "\n";
            $_ = " $'\''";
        }
    }
    print
}' input.txt

Надеюсь, это поможет.

1 голос
/ 05 октября 2019

Это ваш ожидаемый ответ?

echo "Word1 Word2 Word3 Word4, Word5 Word6 Word7 Word8 Word9 Word10, Word11 Word12 Word13 Word14 Word15 Word16 Word17 Word18 Word19 Word20 Word21 and Word22 Word23 Word24." | grep -oE '[a-zA-Z0-9,.]+' | awk '
BEGIN {
    cnt = 0
}
{
    str = str " " $0
    if ($0 ~ /,$/){
        print str
        cnt = 0
        str = ""
    }
    else if (cnt < 10){
        cnt++
    }
    else {
        print str
        cnt = 0
        str = ""
    }
} END {
    print str
}' | sed 's/^ *//'
Word1 Word2 Word3 Word4,
Word5 Word6 Word7 Word8 Word9 Word10,
Word11 Word12 Word13 Word14 Word15 Word16 Word17 Word18 Word19 Word20 Word21
and Word22 Word23 Word24.
...