sed: установить несколько строк ниже с тем же началом - PullRequest
0 голосов
/ 14 апреля 2019

У меня есть текстовый файл следующим образом

# jkakjshkjh
  *   drink  (2 spaces *  2 spaces)(non hash starting)
 *   biscuit  (1 space * 2 spaces)(non hash starting)
* paper       (* 1 space)(non has starting)
... (many more lines) of non hash starting
     *  tea   (7 spaces * 3 space)(non has starting)
# happy
* cup       (* 1 space)(non has starting)
  *   bat  (2 spaces *  2 spaces)(non hash starting)
 *   scooter  (1 space * 2 spaces)(non hash starting)
... (many more lines) of non hash starting
     *  disk   (7 spaces * 3 space)(non has starting)

Я хочу, чтобы вся начальная строка без хеша имела то же начало, что и первая начальная строка без хеша

, то есть:

# jkakjshkjh
  *   drink  (2 spaces *  2 spaces)(non hash starting)
  *   biscuit  (2 spaces *  2 spaces)(non hash starting)
  *   paper  (2 spaces *  2 spaces)(non hash starting)
   ... (many more lines of non hash starting)
  *   tea  (2 spaces *  2 spaces)(non hash starting)
# happy
* cup       (* 1 space)(non has starting)
* bat       (* 1 space)(non has starting)
* scooter       (* 1 space)(non has starting)
... (many more lines) of non hash starting
* disk       (* 1 space)(non has starting)

Теперь есть проблема в вышеуказанной проблеме.

1) Первая нехэш-строка не всегда начинается с (2 пробела * 2 пробела)

Она может варьироваться (1 пробел * 1 пробел) или (число радона в предварительных пробелах * случайно)количество почтовых пробелов)

2) Между ними, если есть строка, начинающаяся с хэша, она не должна касаться этой строки

Так как решить вышеупомянутое с помощью sed

У меня естьпопробовал следующее:

sed -Ez 's/(\n)([^#]\s+\*\s+)([^\n]*\n)([^#]\s+\*\s+)([^\n]*\n)/\1\2\3\2\5/g' filename

выше будет проверять только две последовательные строки.Проблема в том, что он рассматривает 2 строки как одну единицу.Таким образом, группы из двух строк будут иметь одинаковое начало.Но я хочу, чтобы все они имели то же начало, что и первая начальная строка без хеша

Ответы [ 4 ]

2 голосов
/ 15 апреля 2019

Если также доступен awk, вот переносной:

awk 'match($0,/^ *\* */){
  if(b) $0=b substr($0,RLENGTH+1)
  else b=substr($0,1,RLENGTH)
} /^#/{b=""} 1' file

Объяснение

  • b означает начало в том смысле, в каком вы использовали его в своем вопросе (n пробел * n пробел),
  • match($0, /^ *\* */) почти совпадает с /^ *\* */, оно соответствует нулю или большему количеству пробелов, за которыми следует *, за которым следуют ноль или более пробелов в начале $0 (т.е. текущая строка). Если есть совпадение, RSTART и RLENGTH устанавливаются в положение, где начинается соответствующая подстрока и ее длина соответственно,
    • if (b) является сокращением для if (b != "") и безопасен для использования здесь, потому что значение b не может быть 0 в этом случае,
    • $0 = b substr($0, RLENGTH + 1) заменяет начало текущей строки на b,
    • b = substr($0, 1, RLENGTH) устанавливает b на начало текущей строки,
  • /^#/ { b = "" } означает, что если текущая строка начинается с # сброса b,
  • 1 означает печать $0.
1 голос
/ 15 апреля 2019

Я думаю, что это сделает это:

sed '/^#/{N;p;s/.*\n//;s/[^ *].*//;h;d;};s/^[ *]*//;G;s/\(.*\)\n\(.*\)/\2\1/'

Перевод:
Если строка начинается с «#», прочитайте также следующую строку, напечатайте их обе, затем удалите первую строку и все после строки пробелов и звезд, затем поместите то, что осталось (то есть строку пробелов и звезд) в удерживайте пробел и конец.
В противном случае удалите начальные пробелы и звезды, добавьте содержимое пространства удержания (то есть нужную строку пробелов и звездочек), а затем поменяйте местами две части (таким образом, перенеся желаемый префикс вперед).

1 голос
/ 15 апреля 2019

Если у вас все в порядке с решением non-sed: с GNU awk для 3-го аргумента в match ():

$ cat tst.awk
{
    match($0,/^(\s*(\S)\s*)(.*)/,a)
    currHead = a[1]
    currChar = a[2]
    currTail = a[3]
}
currChar == "#" { indent = currHead }
currChar != "#" { indent = (prevChar == "#" ? currHead : indent) }
{ printf "%s%s\n", indent, currTail; prevChar = currChar }

$ awk -f tst.awk file
# jkakjshkjh
  *   drink  (2 spaces *  2 spaces)(non hash starting)
  *   biscuit  (1 space * 2 spaces)(non hash starting)
  *   paper       (* 1 space)(non has starting)
  *   .. (many more lines) of non hash starting
  *   tea   (7 spaces * 3 space)(non has starting)
# happy
* cup       (* 1 space)(non has starting)
* bat  (2 spaces *  2 spaces)(non hash starting)
* scooter  (1 space * 2 spaces)(non hash starting)
* .. (many more lines) of non hash starting
* disk   (7 spaces * 3 space)(non has starting)

С другими awk вы бы просто использовали substr() s, чтобы получить детали, которые match() вставляет a[] для gawk, и используйте [[:space:]] и [^[:space:]] для \s и \S соответственно.

Чтобы помочь вам понять синтаксис, если бы я писал выше на C-подобном языке, то это было бы:

while ( read(FILENAME,line) ) {                 # awk does this for you
    NR++;                                       # awk does this for you
    NF = split(line into $1, $2, $3, ... $NF);  # awk does this for you
    match(line,/^(\s*(\S)\s*)(.*)/,a);
    currHead = a[1];
    currChar = a[2];
    currTail = a[3];
    if (currChar == "#") { indent = currHead; }
    if (currChar != "#") { indent = (prevChar == "#" ? currHead : indent); }
    printf "%s%s\n", indent, currTail; prevChar = currChar;
}                                               # awk does this for you

и фактически вы можете продублировать этот синтаксис в разделе awks BEGIN с помощью:

BEGIN {
    filename = ARGV[1]
    ARGV[1] = ""
    ARGC--
    while ( (getline line < filename) > 0) ) {
        nr++
        nf = split(line,flds)
        match(line,/^(\s*(\S)\s*)(.*)/,a)
        currHead = a[1]
        currChar = a[2]
        currTail = a[3]
        if (currChar == "#") { indent = currHead }
        if (currChar != "#") { indent = (prevChar == "#" ? currHead : indent) }
        printf "%s%s\n", indent, currTail; prevChar = currChar
    }
}

но см. http://awk.freeshell.org/AllAboutGetline, почему бы не сделать это, если у вас нет особых потребностей.

0 голосов
/ 15 апреля 2019

если ваши данные в файле 'd' попробуйте gnu sed,

sed -E ':b /#/{n; p;s/(\s*\*\s*).+/\1/;h;Tb;:l $!N;s/(.*)\n[* ]+(.+)/\1\2/;Tn;p;g;bl;:n D}' d
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...