Как создать однородные столбцы из текста, содержащего как вкладки, так и пробелы?

В простейшей форме, допустим, у меня есть файл с file.txt . Содержимое file.txt выглядит следующим образом:

  Source Destination Maximum To Maximum From Average Total Average To Average From (192.168.1.1) (192.168.1.2) 202.89 Kbps 0 bps 645 bps 645 bps 0 bps 

Я попробовал несколько вариантов команды column , все безрезультатно. Как я могу сделать этот вывод следующим образом:

 Source Destination Maximum To Maximum From Average Total Average To Average From (192.168.1.1) (192.168.1.2) 202.89 Kbps 0 bps 645 bps 645 bps 0 bps 

Мне кажется, что я должен знать, как это сделать, но сейчас я делаю пустой пробел и пока не нашел ничего, что работает, как ожидалось.

EDIT: комментарий и ответ ниже, используя sed отлично подходят для моего первоначального примера (и я действительно предпочитаю решение в комментарии, потому что оно проще и не связано с подключением к tr ). При этом оба решения выполняют точно то же самое в файле с несколькими строками. Фактический file.txt будет содержать сотни строк адресов IPv4 различной длины. Оба sed решения до сих пор вернули следующий результат на исходный (и более точный) file.txt :

Исходный файл.txt:

 Source Destination Maximum To Maximum From Average Total Average To Average From (10.10.10.21) (192.168.123.122) 18.90 Kbps 0 bps 131 bps 131 bps 0 bps (10.10.10.22) (192.168.123.122) 10.88 Kbps 0 bps 23 bps 23 bps 0 bps (10.10.10.23) (192.168.123.123) 10.88 Kbps 0 bps 23 bps 23 bps 0 bps (192.168.123.123) (192.52.168.123) 0 bps 22.84 Kbps 1.17 Kbps 0 bps 1.17 Kbps (192.168.123.124) (192.52.168.123) 0 bps 10.87 Kbps 19 bps 0 bps 19 bps 

Обновлен файл.txt (после использования обоих предлагаемых решений до сих пор):

 Source Destination Maximum To Maximum From Average Total Average To Average From (10.10.10.21) (192.168.123.122) 18.90 Kbps 0 bps 131 bps 131 bps 0 bps (10.10.10.22) (192.168.123.122) 10.88 Kbps 0 bps 23 bps 23 bps 0 bps (10.10.10.23) (192.168.123.123) 10.88 Kbps 0 bps 23 bps 23 bps 0 bps (192.168.123.123) (192.52.168.123) 0 bps 22.84 Kbps 1.17 Kbps 0 bps 1.17 Kbps (192.168.123.124) (192.52.168.123) 0 bps 10.87 Kbps 19 bps 0 bps 19 bps 

Есть ли обновленное решение, которое учитывало бы это смещение?

Скрипт основан на выборочных данных OP.

 sed ' s/\s\s\+/:/g s/\([az)]\)\s\([(0-9A]\)/\1:\2/g ' file.txt | column -s: -t 
  • первое изменение легко найти разделитель (2 или более шагов) :
  • второй найти оставшиеся возможные разделители:
    • между буквой и цифрой
    • после )
    • до A
  • format с разделителем столбца :

Следующий скрипт perl преобразует входные данные в поля, разделенные табуляциями, опираясь на знание о том, что первые два поля имеют только одно слово, а остальные поля имеют по два «слова». Вывод из этого затем передается в column -s $'\t' -t

Это довольно неуклюжий и грубый метод, но он работает.

 #! /usr/bin/perl use strict; while(<>) { my (@F, @fields, $i); @F=split; $fields[0] = $F[0] ; $fields[1] = $F[1] ; for $i (0..4) { $fields[$i + 2] = $F[$i*2 + 2] . ' ' . $F[$i*2 + 3]; } print join("\t",@fields),"\n"; } 

Он используется следующим образом:

 $ ./bandwidth.pl bandwidth.txt | column -s $'\t' -t Source Destination Maximum To Maximum From Average Total Average To Average From (10.10.10.21) (192.168.123.122) 18.90 Kbps 0 bps 131 bps 131 bps 0 bps (10.10.10.22) (192.168.123.122) 10.88 Kbps 0 bps 23 bps 23 bps 0 bps (10.10.10.23) (192.168.123.123) 10.88 Kbps 0 bps 23 bps 23 bps 0 bps (192.168.123.123) (192.52.168.123) 0 bps 22.84 Kbps 1.17 Kbps 0 bps 1.17 Kbps (192.168.123.124) (192.52.168.123) 0 bps 10.87 Kbps 19 bps 0 bps 19 bps 

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

Обновлено: используя копию полного оригинала, называя его в этом примере full_original.txt :

 $ sed 's/\((\)/ \1/g;s/\(Average\)/ \1/g;s/ \([0-9]\)/ \1/g;s/\(\S\) \(\S\)/\1_\2/g' full_original.txt | column -t | tr _ ' ' Source Destination Maximum To Maximum From Average Total Average To Average From (10.10.10.21) (192.168.123.122) 18.90 Kbps 0 bps 131 bps 131 bps 0 bps (10.10.10.22) (192.168.123.122) 10.88 Kbps 0 bps 23 bps 23 bps 0 bps (10.10.10.23) (192.168.123.123) 10.88 Kbps 0 bps 23 bps 23 bps 0 bps (192.168.123.123) (192.52.168.123) 0 bps 22.84 Kbps 1.17 Kbps 0 bps 1.17 Kbps (192.168.123.124) (192.52.168.123) 0 bps 10.87 Kbps 19 bps 0 bps 19 bps 

объяснение

Большая часть этого решения использует подход «разделяй и властвуй», когда у вас есть несколько отдельных проблем, обращаясь к ним индивидуально. Затем он собирается в конце с помощью команды волшебного column с последним касанием tr :

  • базовый шаблон – это s/searchstring/replacestring/g' , g для жадного / глобального, поэтому применяется для всех совпадений не только для первого
  • мы используем группировку, поэтому \(somegroup\) в части поиска может быть перепечатана через \1 если это первая группа, \2 если это вторая группа и т. д.
  • полуколонна ; позволяет нам добавлять несколько команд поиска и замены в один экземпляр sed , более эффективный, чем трубопровод, и, таким образом, запускать несколько seds, например sed command | sed command | sed command .... sed command | sed command | sed command .... sed command | sed command | sed command .... т. д.
  • s/\((\)/ \1/g имеет дело с ...123) (19... нажатие двух значений, заключенных в круглые скобки, более одного места друг от друга, чтобы избежать проблемы смещения, обнаруженного OP. делает это, сопоставляя любую открывающую скобку ( и префикс с пространством, чтобы он стал space + (
  • s/\(Average\)/ \1/g – это часть, которая имеет отношение к тому, как Maximum From Average Total не имеет специального разделения, что затрудняет последующий поиск и замену, поэтому сначала мы применяем дополнительное пространство перед каждым возникновением Average
  • s/ \([0-9]\)/ \1/g чтобы отделить значения полей в исходном тексте 645 bps 645 bps 0 bps с, префиксное любое появление space+number пробелом, чтобы оно стало space+space+number , снова, чтобы помочь более поздней команде sed рассказать им обособленно
  • последняя команда sed s/\(\S\) \(\S\)/\1_\2/g является обходным решением, она ищет non-space+space+non-space и с группировкой меняет его так, что пространство превратился в символ подчеркивания. Это удерживает Maximum To вместе для более поздней команды column мы используем, поэтому она становится Maximum_To
  • | column -t | column -t передает его команде столбца, которая по умолчанию в man column говорит: By default, the column command will merge multiple adjacent delimiters into a single delimiter when using the -t option чтобы обрабатывать переменное пространство между текстами как один разделитель ,
  • столбец также выполняет повторное форматирование для выравнивания текста
  • наконец | tr _ ' ' | tr _ ' ' отменяет обходной путь преобразования пространств в подчеркивание ( _ ) с помощью команды tr , преобразует все _ , обратно в космос ' ' .

И таким образом у вас есть выход, который вы хотите.