Как отобразить весь файл с операторами #include

Я предполагаю, что каждый пользователь во входном файле (в столбце «Пользователь» )только один раз. Далее я предполагаю, что вертикальная черта(|)разделяет на самом деле находятся в файле, и что они всегда отделяются от данных пробелом, и что строка заголовка не на самом деле присутствует.

Вот двухпроходное -решение с использованием awk. Первый проход строит массив, содержащий супервайзеров всех; второй проход формирует вывод:

awk 'pass==1 { super[$1] = $3; }
     pass==2 {
                print
                user=$3
                while (super[user] != "") {
                        print $1, "|", super[user], "|", $5
                        user=super[user]
                }
             }
    ' pass=1 data pass=2 data

Это приведет к неправильному выравниванию вывода. Чтобы исправить это, передайте его через column -t. Или мы можем отформатировать вывод из скрипта awk; если вы этого хотите, укажите нужные правила форматирования.

Между прочим, эта операция широко известна как транзитивное замыкание .

1
19.09.2019, 17:16
4 ответа

небольшой скрипт может решить литерал #include,
не рекурсивно, включение самого файла может вызвать бесконечный цикл
и рекурсивное включение не указано как обязательное.

В конечном итоге может понадобиться настоящий синтаксический анализатор, и вы можете обратиться к другим ответам.

передать main.txtследующему Perl-скрипту через стандартный ввод
вывод (примерно )желаемый вывод.
awk тоже может сделать эту работу, но мне нравится тайна perl:)

#!/usr/bin/perl
while (<>) {
  if (/^#include (.*)/) {
    system('cat',$1);
  } else {
    print;
  }
}

Укороченная версия приведенного выше кода с одним -вкладышем:

perl -e '(/^#include (.*)/ ? system("cat",$1) : print) while <>;' <main.txt

Для тех, кому интересно узнать больше о Perl:

  • <>— это сокращение от STDIN, которое является стандартным входным потоком

  • .
  • Ключевое слово whileможет быть помещено в конце строки; и ()можно опустить, если нет двусмысленной интерпретации.

  • Для каждой строки, прочитанной из STDIN, Perl сохраняет ее в$_

  • $_— это предопределенная переменная, которую большинство Perl встроенных -функций принимает в качестве аргумента, если аргумент не задан,

  • print— одна из встроенных -функций, которая принимает$_

  • После прочтения каждой строки perl будет вычислять троичное выражение. Соответствие регулярному выражению применяется к $_по умолчанию

  • .
  • Когда /^#include (.*)/возвращает 1 (число обнаруженных совпадений ), указывает, что $_начинается с литерала#include

  • perl хранит оставшуюся часть от $_до $1,так как это 1-й сгруппированный захват ($2,$3....определяются, если групп больше)

  • Функция systemвызывает команду через системную оболочку, в основномsh
    в этой строке вызывается catи передается $1в качестве аргумента.

  • Когда /^#include (.*)/возвращает 0 на $_, printоценивается и в качестве побочного эффекта выводит$_

  • Значение троичного выражения молча игнорируется

1
27.01.2020, 23:22

Если вы собираетесь использовать синтаксис #include "file"как в программировании на C язык, который вы могли бы использовать cppдля его разбора. Скажем, если ваш main.txtвыглядел так:

Some text

#include "fragment1.txt"

Some other text

#include "fragment2.txt"

Some other text

ты мог бы сделать:

$ cpp -nostdinc -P <main.txt
Some text
This is content of fragment1
Some other text
This is content of fragment2
Some other text

Как поясняется вman cpp:

-nostdinc Do not search the standard system directories for header files. Only the directories you have specified with -I options (and the directory of the current file, if appropriate) are searched.

-P Inhibit generation of linemarkers in the output from the preprocessor. This might be useful when running the preprocessor on something that is not C code, and will be sent to a program which might be confused by the linemarkers.

Обратите внимание, однако, что cppделает больше, чем анализирует #includeстрок и в зависимости от вашего варианта использования это может быть или не быть полезным. Для например, cppавтоматически удаляет все комментарии в стиле C -:

// this is a comment
/* this as a comment too /*

Это выглядит полезным, и если вы хотите сохранить комментарии, вы можете использовать опцию -C. Другое дело что cppпопытается интерпретировать все строки, начинающиеся с #, как директивы препроцессора. Например, это:

#define A 7
Value: A

будет напечатано как:

Value: 7

и это

#blah blah

выдаст ошибку:

<stdin>:14:2: error: invalid preprocessing directive #blah

На практике существуют некоторые программы, использующие cpp. парсер конфигов, например xrdb.

2
27.01.2020, 23:22

Запуск препроцессора C напрямую:

$ cpp -P main.txt

Это будет сделано. Вам нужно будет указать имя включаемых файлов в кавычках:

#включают "fragment1.txt"

В качестве бонуса вы получаете всю мощь препроцессора C :макросов, условных включений и т. д.

0
27.01.2020, 23:22

В GNU sedвы можете использовать флаг eдля команды s:

sed 's/^#include/cat/e'

Заменяет #includeна catи выполняет его, поэтому строка заменяется ответом команды cat.

Если имя файла может содержать такие символы, как пробелы, лучше заключить его в кавычки:

sed 's/^#include *\(.*\)/cat "\1"/e'

Но имейте в виду, что это все еще может привести к ошибке для специальных имен файлов с обратной косой чертой, тиками и некоторыми другими.

0
27.01.2020, 23:22

Теги

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