Я предполагаю, что каждый пользователь во входном файле (в столбце «Пользователь» )только один раз. Далее я предполагаю, что вертикальная черта(|
)разделяет на самом деле находятся в файле, и что они всегда отделяются от данных пробелом, и что строка заголовка не на самом деле присутствует.
Вот двухпроходное -решение с использованием 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
; если вы этого хотите, укажите нужные правила форматирования.
Между прочим, эта операция широко известна как транзитивное замыкание .
небольшой скрипт может решить литерал #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
оценивается и в качестве побочного эффекта выводит$_
Значение троичного выражения молча игнорируется
Если вы собираетесь использовать синтаксис #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
.
Запуск препроцессора C напрямую:
$ cpp -P main.txt
Это будет сделано. Вам нужно будет указать имя включаемых файлов в кавычках:
#включают "fragment1.txt"
В качестве бонуса вы получаете всю мощь препроцессора C :макросов, условных включений и т. д.
В GNU sed
вы можете использовать флаг e
для команды s
:
sed 's/^#include/cat/e'
Заменяет #include
на cat
и выполняет его, поэтому строка заменяется ответом команды cat
.
Если имя файла может содержать такие символы, как пробелы, лучше заключить его в кавычки:
sed 's/^#include *\(.*\)/cat "\1"/e'
Но имейте в виду, что это все еще может привести к ошибке для специальных имен файлов с обратной косой чертой, тиками и некоторыми другими.