Преобразовать строку в столбцы

«Именованный канал» на самом деле является очень точным названием того, чем он является — он похож на обычный канал, за исключением того, что он имеет имя (в файловой системе ).

Канал — обычный, не -названный («анонимный» ), используемый в some-command | grep pattern, — это файл особого типа. И я имею в виду файл, вы читаете и пишете в него так же, как и в любой другой файл. Grep на самом деле не волнует¹, что он читает из канала, а не из терминала³ или из обычного файла.

Технически за кулисами происходит то, что stdin, stdout и stderr — это три открытых файла, (файловые дескрипторы ), которые передаются при каждом запуске команды. Дескрипторы файлов (, которые используются в каждом системном вызове для чтения/записи/и т. д. файлы )— это просто числа; stdin, stdout и stderr — это файловые дескрипторы 0, 1 и 2. Итак, когда ваша оболочка устанавливает some-command | grep, она делает нечто следующее:

  1. Запрашивает у ядра анонимный канал. Имени нет, поэтому это нельзя сделать с помощью open, как для обычного файла — вместо этого это делается с помощью pipeили pipe2, которые возвращают два файловых дескриптора.⁴

  2. Разветвляет дочерний процесс(fork()создает копию родительского процесса; здесь открыты обе стороны канала ), копирует сторону записи -канала в fd 1 (stdout ). Ядро имеет системный вызов для копирования номеров файловых дескрипторов; это dup2()или dup3(). Затем он закрывает сторону чтения и другую копию стороны записи. Наконец, он использует execveдля выполнения some-command. Так как канал равен fd 1, стандартный вывод some-commandявляется каналом.

  3. Вилки другого дочернего процесса. На этот раз он дублирует сторону чтения канала на fd 0 (stdin )и выполняет grep. Таким образом, grep будет читать из канала как стандартный ввод.

  4. Затем он ожидает выхода обоих дочерних элементов.

  5. В этот момент ядро ​​замечает, что канал больше не открыт, и собирает мусор. Вот что на самом деле разрушает трубу.

Именованный канал просто дает этому анонимному каналу имя, помещая его в файловую систему. Итак, теперь любой процесс в любой момент в будущем может получить дескриптор файла для канала с помощью обычного openсистемного вызова. Концептуально канал не будет уничтожен до тех пор, пока все читатели/писатели не закроют его и он unlinkне будет удален из файловой системы.²

Вот так, кстати, работают файлы в Unix.unlink(системный вызовrm)просто удаляет одно из имен файла; только когда все имена удалены и файл не открыт, он действительно будет удален. Несколько ответов здесь исследуют это:

СНОСКИ

  1. Технически это, вероятно, неверно — возможно, можно провести некоторую оптимизацию, зная, и фактические реализации grep часто сильно оптимизированы. Но концептуально это не имеет значения (и, действительно, прямая реализация grep не будет ).
  2. Конечно, ядро ​​на самом деле не постоянно хранит все структуры данных в памяти, а прозрачно воссоздает их всякий раз, когда первая программа открывает именованный канал (, а затем сохраняет их до тех пор, пока он открыт ). Так что, как если бы они существовали столько же, сколько и имя.
  3. Terminal не является обычным местом для чтения grep, но это стандартный стандартный ввод, если вы не укажете другой. Поэтому, если вы наберете только grep patternв своей оболочке, grepбудет считываться с терминала. Единственное, что приходит на ум, это если вы собираетесь что-то вставить в терминал.
  4. В Linuxанонимные каналы на самом деле создаются в специальной файловой системе, pipefs. Подробнее см. Как работают каналы в Linux . Обратите внимание, что это внутренняя деталь реализации Linux.

-1
28.04.2021, 09:32
2 ответа
$ awk -v RS='' '{ $1 = $1; print }' file
HEAD1 IF FI GH
HEAD2 PU GT
HEAD3 FG DF YT GU

Когда RS, разделитель входных записей, пуст, а не установлен по умолчанию, символ новой строки awkразделит ввод на записи на основе появления двух или более последовательных символов новой строки во вводе, т.е. больше пустых строк будет считаться концом записи. Обычно это называется переводом awkв «режим абзаца».

Затем код устанавливает первое поле в себя. Это похоже на операцию -без операции, но она провоцирует awkна повторное -формирование текущей записи для вывода. Значения по умолчанию дляORS(разделителя выходных записей )иOFS(разделителя выходных полей )используются (символ новой строки и символ пробела соответственно ), поэтому при печати записи все поля печатаются на одной строке с пробелами в -между ними, заканчиваясь в конце символом новой строки.

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

$ awk -v RS='' -v OFS='\t' '{ $1 = $1; print }' file
HEAD1   IF      FI      GH
HEAD2   PU      GT
HEAD3   FG      DF      YT      GU
$ awk -v RS='' -v OFS=',' '{ $1 = $1; print }' file
HEAD1,IF,FI,GH
HEAD2,PU,GT
HEAD3,FG,DF,YT,GU
$ awk -v RS='' -v OFS='::' '{ $1 = $1; print }' file
HEAD1::IF::FI::GH
HEAD2::PU::GT
HEAD3::FG::DF::YT::GU

Код awkможно было бы сократить до { $1 = $1 }; 1, где замыкающий 1приводил бы к печати текущей записи путем безусловного вызова действия по умолчанию. Это довольно распространенный способ безусловной печати текущей записи.


Использование sedвместо:

$ sed -e '/./ { H; $!d; }' -e 'x; y/\n/ /; s/.//' file
HEAD1 IF FI GH
HEAD2 PU GT
HEAD3 FG DF YT GU

Добавляет текущую строку в область хранения с H, если строка содержит что-либо. Строка добавляется к пространству удержания с помощью разделительного символа новой строки. Если это была не последняя строка ввода, пробелы шаблонов отбрасываются с помощью d, и мы сразу же продолжаем с начала со следующей строки ввода.

Если текущая строка пуста, мы меняем место хранения на x(, что, поскольку пространство шаблона пусто, также приводит к очистке пространства хранения ), заменяет все новые строки пробелами и lop от первого символа (, который будет дополнительным символом пробела ).

4
28.04.2021, 22:51

С помощью awkв режиме абзаца:

awk -v RS= -v FS='\n' -v OFS=' ' '{
        for (i=1;i<=NF;i++) {
        printf "%s%s", $i, (i<NF ? OFS : ORS)
    }
}' file
HEAD1 IF FI GH
HEAD2 PU GT
HEAD3 FG DF YT GU
  • RS=При такой установке RSвходные записи разделяются пустыми строками.
  • FS='\n'Таким образом, вы можете рассматривать каждую строку в каждом абзаце как поле записи.
  • с forпроходим по всем полям и с printfпечатаем их под условным выражением, известным какternary expression:если exp1 i<NFвозвращает true, OFSвыполняется; в противном случае выполняется ORS.
1
28.04.2021, 22:51

Теги

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