Команда paste объединяет несколько входных файлов для создания из них нового текстового файла с разделителями. Мы рассмотрим, как эффективно использовать команду paste в Linux и Unix.
В самом основном случае использования команда paste принимает N входных файлов и присоединяется к ним по строкам на выходе:
sh$ printf "%s\n" {a..e} | tee letters a b c d e sh$ printf "%s\n" {1..5} | tee digits 1 2 3 4 5 sh$ paste letters digits a 1 b 2 c 3 d 4 e 5
Но давайте теперь дадим теоретические объяснения для практического примера. Если вы загрузили образцы файлов, вы увидите, что у нас есть несколько файлов данных, соответствующих различным столбцам таблицы:
sh$ head -3 *.csv ==> ACCOUNTLIB.csv <== ACCOUNTLIB TIDE SCHEDULE VAT BS/ENC ==> ACCOUNTNUM.csv <== ACCOUNTNUM 623477 445452 ==> CREDIT.csv <== CREDIT <--- empty line <--- empty line ==> DEBIT.csv <== DEBIT 00000001615,00 00000000323,00
Из этих данных довольно легко создать текстовый файл с разделителями табуляции:
sh$ paste *.csv | head -3 ACCOUNTLIB ACCOUNTNUM CREDIT DEBIT TIDE SCHEDULE 623477 00000001615,00 VAT BS/ENC 445452 00000000323,00
Как вы можете видеть, при отображении на консоли содержимое этого файла с разделителями разделов не создает идеально отформатированную таблицу. Но это по дизайну: команда paste не используется для создания текстовых файлов с фиксированной шириной, а только текстовые файлы с разделителями, где одному заданному символу назначается роль разделителя полей.
Таким образом, даже если это не очевидно в выводе выше, на самом деле между каждым полем есть один и только один символ табуляции. Давайте сделаем это с помощью команды sed :
sh$ paste *.csv | head -3 | sed -n l ACCOUNTLIB\tACCOUNTNUM\tCREDIT\tDEBIT$ TIDE SCHEDULE\t623477\t\t00000001615,00$ VAT BS/ENC\t445452\t\t00000000323,00$
Теперь невидимые символы отображаются однозначно на выходе. И вы можете видеть символы табуляции, отображаемые как \t. Вы можете их подсчитать: на каждой выходной строке всегда есть три вкладки — одна между каждым полем. И когда вы видите два из них подряд, это означает, что там было пустое поле. Это часто бывает в файлах наших конкретных примеров, поскольку в каждой строке задано либо поле CREDIT, либо DEBIT, но оба они одновременно.
Как мы видели, команда paste использует символ табуляции в качестве разделителя полей по умолчанию («разделитель»). Что-то, что мы можем изменить, используя эту опцию -d. Предположим, я хотел бы вместо этого использовать полуточку:
# Кавычки вокруг ';' используются для # оболочки считать эту точку с запятой команды separator sh$ paste -d ';' *.csv | head -3 ACCOUNTLIB;ACCOUNTNUM;CREDIT;DEBIT TIDE SCHEDULE;623477;;00000001615,00 VAT BS/ENC;445452;;00000000323,00
Нет необходимости добавлять команду sed в конце конвейера здесь, так как разделитель, который мы использовали, является печатным символом. Во всяком случае, результат тот же: в данной строке каждое поле отделяется от своего соседа с помощью односимвольного разделителя.
В приведенных выше примерах есть одна общая черта: команда paste считывает все свои входные файлы параллельно, что требуется, поэтому она может объединять их по очереди на выходе.
Но команда paste также может работать в так называемом последовательном режиме, с использованием флага -s. Как следует из названия, в последовательном режиме команда paste будет считывать входные файлы один за другим. Содержимое первого входного файла будет использоваться для создания первой выходной строки. Затем содержимое второго входного файла будет использоваться для создания второй выходной строки и т. д. Это также означает, что на выходе будет столько строк, сколько файлов на входе.
Более формально данные, взятые из файла N, будут отображаться в виде N-й строки на выходе в последовательном режиме, тогда как он будет отображаться как N-й столбец в режиме «параллельного» по умолчанию. В математических терминах таблица, полученная в последовательном режиме, представляет собой транспонирование таблицы, созданной в режиме по умолчанию (и наоборот).
Чтобы проиллюстрировать это, рассмотрим небольшую подвыборку наших данных:
sh$ head -5 ACCOUNTLIB.csv | tee ACCOUNTLIB.sample ACCOUNTLIB TIDE SCHEDULE VAT BS/ENC PAYABLES ACCOMMODATION GUIDE sh$ head -5 ACCOUNTNUM.csv | tee ACCOUNTNUM.sample ACCOUNTNUM 623477 445452 4356 623372
В режиме по умолчанию («параллельный») данные входного файла будут выступать в качестве столбцов на выходе, создавая таблицу из двух столбцов на пять строк:
sh$ paste *.sample ACCOUNTLIB ACCOUNTNUM TIDE SCHEDULE 623477 VAT BS/ENC 445452 PAYABLES 4356 ACCOMMODATION GUIDE 623372
Но в последовательном режиме данные входного файла будут отображаться в виде строк, создавая теперь таблицу из пяти столбцов на две строки:
sh$ paste -s *.sample ACCOUNTLIB TIDE SCHEDULE VAT BS/ENC PAYABLES ACCOMMODATION GUIDE ACCOUNTNUM 623477 445452 4356 623372
Как и многие стандартные утилиты, команда paste может использовать стандартный ввод для чтения данных. Либо неявно, когда нет имени файла, заданного в качестве аргумента, либо явно с помощью специального имени файла. По-видимому, это не так полезно:
# Здесь команда paste бесполезна head -5 ACCOUNTLIB.csv | paste ACCOUNTLIB TIDE SCHEDULE VAT BS/ENC PAYABLES ACCOMMODATION GUIDE
Мы рекомендуем вам протестировать его самостоятельно, но следующий синтаксис должен приводить к тому же результату, что и команда paste в этом случае бесполезна:
head -5 ACCOUNTLIB.csv | paste -
Итак, какова может быть точка чтения данных со стандартного ввода? Ну, с флагом -s, все становится намного интереснее, как мы это увидим сейчас.
Как мы видели в параграфах ранее, в последовательном режиме команда paste будет записывать все строки входного файла на той же выходной строке. Это дает нам простой способ объединить все строки, считанные со стандартного ввода, только в одну (потенциально очень длинную) выходную строку:
sh$ head -5 ACCOUNTLIB.csv | paste -s -d':' ACCOUNTLIB:TIDE SCHEDULE:VAT BS/ENC:PAYABLES:ACCOMMODATION GUIDE
Это в основном то же самое, что вы могли бы сделать с помощью команды tr, но с одной разницей. Давайте используем утилиту diff, чтобы определить:
sh$ diff <(head -5 ACCOUNTLIB.csv | paste -s -d':') \ <(head -5 ACCOUNTLIB.csv | tr '\n' ':') 1c1 < ACCOUNTLIB:TIDE SCHEDULE:VAT BS/ENC:PAYABLES:ACCOMMODATION GUIDE --- > ACCOUNTLIB:TIDE SCHEDULE:VAT BS/ENC:PAYABLES:ACCOMMODATION GUIDE: \ Нет новой строки в конце файла
Как сообщается утилитой diff, мы можем видеть, что команда tr заменила каждый экземпляр символа новой строки на данный разделитель, в том числе самый последний. С другой стороны, команда paste сохранила последний символ новой строки без изменений. Так что, если вам нужен разделитель после самого последнего поля или нет, вы будете использовать одну команду или другую.
Согласно спецификации Open Group, «стандартный ввод должен читать одну строку за один раз» с помощью команды paste. Таким образом, передача нескольких вхождений имени -специального файла в качестве аргументов в команду paste приведет к тому, что несколько последовательных строк ввода будут записаны в одну и ту же выходную строку:
sh$ seq 9 | paste - - - 1 2 3 4 5 6 7 8 9
Чтобы сделать все более ясным, мы рекомендуем вам изучить разницу между двумя приведенными ниже командами. В первом случае команда paste открывает три раза один и тот же файл, что приводит к дублированию данных на выходе. С другой стороны, во втором случае файл ACCOUNTLIB открывается только один раз (оболочкой), но три раза считывается для каждой строки (по команде paste), в результате чего содержимое файла отображается в виде трех столбцов:
sh$ paste ACCOUNTLIB.csv ACCOUNTLIB.csv ACCOUNTLIB.csv | head -2 ACCOUNTLIB ACCOUNTLIB ACCOUNTLIB TIDE SCHEDULE TIDE SCHEDULE TIDE SCHEDULE sh$ paste - - - < ACCOUNTLIB.csv | head -2 ACCOUNTLIB TIDE SCHEDULE VAT BS/ENC PAYABLES ACCOMMODATION GUIDE VAT BS/ENC
Учитывая поведение команды paste при чтении со стандартного ввода, обычно не рекомендуется использовать несколько специальных имен файлов в последовательном режиме. В этом случае первое вхождение будет читать стандартный ввод до его окончания, а последующие вхождения будут считываться из уже исчерпанного входного потока, что приведет к отсутствию большего количества данных:
# Следующая команда создаст 3 строки вывода. # Но первый исчерпал стандартный ввод, # остались две строки пусты sh$ seq 9 | paste -s - - - 1 2 3 4 5 6 7 8 9
Спецификации Open Group для утилиты paste достаточно ясны:
Если условие конца файла обнаружено в одном или нескольких входных файлах, но не во всех входных файлах, вставка должна вести себя так, как если бы пустые строки были прочитаны из файлов, на которых был обнаружен конец файла, если только установлен параметр -s.
Итак, поведение — это то, что вы можете ожидать: недостающие данные заменяются «пустым» контентом. Чтобы проиллюстрировать это поведение, давайте запишем еще пару транзакций в нашу «базу данных». Чтобы сохранить исходные файлы неповрежденными, мы будем работать над копией наших данных:
# Скопировать файлы sh$ for f in ACCOUNTNUM ACCOUNTLIB CREDIT DEBIT; do cp ${f}.csv NEW${f}.csv done # Обновление копии sh$ cat - << EOF >> NEWACCOUNTNUM.csv 1080 4356 EOF sh$ cat - << EOF >> NEWDEBIT.csv 00000001207,35 EOF sh$ cat - << EOF >> NEWCREDIT.csv 00000001207,35 EOF
С этими обновлениями мы теперь зарегистрировали новое движение капитала со счета № 1080 на счет № 4356. Однако, как вы, возможно, заметили, я не стал обновлять файл ACCOUNTLIB. Это не кажется такой большой проблемой, потому что команда paste заменит недостающие строки пустыми данными:
sh$ paste -d';' NEWACCOUNTNUM.csv \ NEWACCOUNTLIB.csv \ NEWDEBIT.csv \ NEWCREDIT.csv | tail 4356;PAYABLES;;00000000402,03 613866;RENTAL COSTS;00000000018,00; 4356;PAYABLES;;00000000018,00 657991;MISCELLANEOUS CHARGES;00000000015,00; 445333;VAT BS/DEBIT;00000000003,00; 4356;PAYABLES;;00000000018,00 626510;LANDLINE TELEPHONE;00000000069,14; 445452;VAT BS/ENC;00000000013,83; 1080;;00000001207,35; # <-- здесь отсутствует метка учетной записи 4356;;;00000001207,35 # <-- здесь отсутствует метка учетной записи
Но будьте осторожны, команда paste может соответствовать линиям только по их физическому положению: все, что он может сказать, это файл «короче», чем другой. Не там, где данные отсутствуют. Поэтому он всегда добавляет поля пробелов в конце вывода, что может привести к неожиданным смещениям в ваших данных. Давайте сделаем это очевидным, добавив еще одну транзакцию:
sh$ cat << EOF >> NEWACCOUNTNUM.csv 4356 3465 EOF sh$ cat << EOF >> NEWACCOUNTLIB.csv PAYABLES WEB HOSTING EOF sh$ cat << EOF >> NEWDEBIT.csv 00000000706,48 EOF sh$ cat << EOF >> NEWCREDIT.csv 00000000706,48 EOF
На этот раз я был более строгим, так как я правильно обновил номер учетной записи (ACCOUNTNUM) и соответствующий ярлык (ACCOUNTLIB), а также файлы данных CREDIT и DEBIT. Но поскольку в предыдущей записи отсутствовали данные, команда paste больше не может сохранять связанные поля в одной строке:
sh$ paste -d';' NEWACCOUNTNUM.csv \ NEWACCOUNTLIB.csv \ NEWDEBIT.csv \ NEWCREDIT.csv | tail 4356;PAYABLES;;00000000018,00 657991;MISCELLANEOUS CHARGES;00000000015,00; 445333;VAT BS/DEBIT;00000000003,00; 4356;PAYABLES;;00000000018,00 626510;LANDLINE TELEPHONE;00000000069,14; 445452;VAT BS/ENC;00000000013,83; 1080;PAYABLES;00000001207,35; 4356;WEB HOSTING;;00000001207,35 4356;;;00000000706,48 3465;;00000000706,48;
Как вы можете видеть, учетная запись № 4356 сообщается с надписью «WEB HOSTING», тогда как в действительности последняя должна появиться в строке, соответствующей учетной записи № 3465.
В заключение, если вам приходится иметь дело с отсутствующими данными, вместо команды paste вы должны использовать эту joinутилиту, так как последняя будет соответствовать строкам на основе их содержимого, а не на основе позиции во входном файле. Это делает его гораздо более подходящим для приложений типа «база данных». Я уже опубликовал видео о команде join, но это, вероятно, заслуживает отдельной статьи, поэтому дайте нам знать, если вас интересует эта тема!
В подавляющем большинстве случаев использования вы укажете только один символ в качестве разделителя. Это то, что мы сделали до сих пор. Однако, если вы укажете несколько символов после опции -d, команда paste будет циклически перебирать их: первый символ будет использоваться как первый разделитель поля в строке, второй символ — второй разделитель полей и т. д.
sh$ paste -d':+-' ACCOUNT*.csv CREDIT.csv DEBIT.csv | head -5 ACCOUNTLIB:ACCOUNT NUM+CREDIT-DEBIT TIDE SCHEDULE:623477+-00000001615,00 VAT BS/ENC:445452+-00000000323,00 PAYABLES:4356+00000001938,00- ACCOMODATION GUIDE:623372+-00000001333,00
Разделители полей могут появляться только между полями. Не в конце строки. И вы не можете вставить более одного разделителя между двумя заданными полями. В качестве трюка для преодоления этих ограничений вы можете использовать /dev/null специальный файл в качестве дополнительного ввода, где вам нужен дополнительный разделитель:
# Покажите начало скобки между # полем ACCOUNTLIB и полем ACCOUNTNUM, и # закрывающая скобка между полем ACCOUNTNUM # и пустое поле `/dev/null`: sh$ paste -d'()' \ ACCOUNT*.csv /dev/null | head -5 ACCOUNTLIB(ACCOUNTNUM) TIDE SCHEDULE(623477) VAT BS/ENC(445452) PAYABLES(4356) ACCOMODATION GUIDE(623372)
Вы можете даже злоупотреблять:
sh$ paste -d'# is ' \ - ACCOUNTNUM.csv - - - ACCOUNTLIB.csv < /dev/null | tail -5 #657991 is MISCELLANEOUS CHARGES #445333 is VAT BS/DEBIT #4356 is PAYABLES #626510 is LANDLINE TELEPHONE #445452 is VAT BS/ENC
Однако нет необходимости говорить, что если вы достигнете такого уровня сложности, это может быть ключом к тому, что pasteутилита не обязательно является лучшим инструментом для работы. Возможно, стоит подумать, в таком случае, что-то вроде команды awksed или awk.
Но что, если в списке содержится меньше разделителей, чем нужно для отображения строки на выходе? Интересно, что команда paste будет «зацикливаться» над ними. Итак, как только список исчерпан, команда paste вернется к первому разделителю, что, вероятно, откроет дверь для какого-то творческого использования. Что касается меня, я не смог сделать что-то действительно полезное с этой функцией, учитывая мои данные. Поэтому вам нужно будет удовлетвориться следующим немного надуманным примером. Но это не будет полной потерей вашего времени, так как это был хороший повод упомянуть, что вам нужно удвоить обратную косую черту (\\), когда вы хотите использовать ее как разделитель:
sh$ paste -d'/\\' \ - ACCOUNT*.csv CREDIT.csv DEBIT.csv - < /dev/null | tail -5 /MISCELLANEOUS CHARGES\657991/\00000000015,00/ /VAT BS/DEBIT\445333/\00000000003,00/ /PAYABLES\4356/00000000018,00\/ /LANDLINE TELEPHONE\626510/\00000000069,14/ /VAT BS/ENC\445452/\00000000013,83/
Как и большинство стандартных утилит Unix, команда paste родилась за один символ, эквивалентный одному байту. Но это уже не так: сегодня многие системы используют кодировку переменной длины UTF-8 по умолчанию. В UTF-8 символ может быть представлен 1, 2, 3 или 4 байтами. Это позволяет нам смешивать в том же текстовом файле все разнообразие человеческой записи, а также множество символов и эмоций — при сохранении совместимости по возрастанию с унаследованной однобайтовой кодировкой символов US-ASCII.
Скажем, например, мы хотели бы использовать WHITE DIAMOND (◇ U + 25C7) в качестве нашего разделителя полей. В UTF-8 этот символ кодируется с использованием трех байтов e2 97 87. Этот символ может быть трудно получить с клавиатуры, поэтому, если вы хотите попробовать это самостоятельно, мы предлагаем вам скопировать его в блок кода ниже:
# Часть sed используется только как небольшой трюк, чтобы добавить # номер строки в качестве первого поля в выводе sh$ sed -n = ACCOUNTNUM.csv | paste -d'◇' - ACCOUNT*.csv | tail -5 26MISCELLANEOUS CHARGES657991 27VAT BS/DEBIT445333 28PAYABLES4356 29LANDLINE TELEPHONE626510 30VAT BS/ENC445452
Довольно обманчиво, не так ли? Вместо ожидаемого белого бриллианта у нас есть символ «знак вопроса» (по крайней мере, так оно отображается в нашей системе). Однако это не «случайный» символ. Это символ замены Unicode, используемый «для указания проблем, когда система не может отобразить поток данных в правильный символ». Итак, что пошло не так?
Еще раз, изучение исходного двоичного содержимого вывода даст нам несколько подсказок:
sh$ sed -n = ACCOUNTNUM.csv | paste -d'◇' - ACCOUNT*.csv | tail -5 | hexdump -C 00000000 32 36 e2 4d 49 53 43 45 4c 4c 41 4e 45 4f 55 53 |26.MISCELLANEOUS| 00000010 20 43 48 41 52 47 45 53 97 36 35 37 39 39 31 0a | CHARGES.657991.| 00000020 32 37 e2 56 41 54 20 42 53 2f 44 45 42 49 54 97 |27.VAT BS/DEBIT.| 00000030 34 34 35 33 33 33 0a 32 38 e2 50 41 59 41 42 4c |445333.28.PAYABL| 00000040 45 53 97 34 33 35 36 0a 32 39 e2 4c 41 4e 44 4c |ES.4356.29.LANDL| 00000050 49 4e 45 20 54 45 4c 45 50 48 4f 4e 45 97 36 32 |INE TELEPHONE.62| 00000060 36 35 31 30 0a 33 30 e2 56 41 54 20 42 53 2f 45 |6510.30.VAT BS/E| 00000070 4e 43 97 34 34 35 34 35 32 0a |NC.445452.| 0000007a
У нас уже была возможность потренироваться с шестнадцатеричными дампами выше, поэтому ваши глаза теперь должны быть нацелены на то, чтобы определить полевые разделители в потоке байтов. Если посмотреть внимательно, вы увидите разделитель полей после того, как номер строки будет байтом e2. Но если вы продолжите свои исследования, вы заметите, что второй разделитель полей 97. Не только команда paste не выводила символ, который мы хотели, но он также не использовал везде тот же байт, что и разделитель?!?
Подождите минуту: разве это не напоминает вам то, о чем мы уже говорим? И эти два байта e2 97, разве они вам не знакомы? Ну, знакомый, вероятно, немного слишком много, но если вы отскакиваете несколько абзацев, вы можете найти их где-то где-то…
Так вы нашли, где это было? Ранее мы сказали, сто белый бриллиант кодируется в UTF-8 как три байта e2 97 87. И действительно, команда paste рассматривала эту последовательность не как целый трехбайтовый символ, а как три независимых байта, и поэтому он использовал первый байт в качестве первого разделителя полей, затем второй байт в качестве второго разделителя полей.
Мы позволим вам повторить этот эксперимент, добавив еще один столбец во входные данные; вы должны увидеть третий разделитель полей 87- третий байт представления UTF-8 для белого алмаза.
Хорошо, это объяснение: команда paste принимает только однобайтовые «символы» в качестве разделителя. И это особенно раздражает, так как еще раз мы не знаем, как преодолеть это ограничение, кроме как с помощью трюка /dev/null, который мы уже дали вам:
sh$ sed -n = ACCOUNTNUM.csv | paste -d'◇' \ - /dev/null /dev/null \ ACCOUNTLIB.csv /dev/null /dev/null \ ACCOUNTNUM.csv | tail -5 26◇MISCELLANEOUS CHARGES◇657991 27◇VAT BS/DEBIT◇445333 28◇PAYABLES◇4356 29◇LANDLINE TELEPHONE◇626510 30◇VAT BS/ENC◇445452
В настоящее время утилита игнорирует многобайтовые символы в списке разделителей и допускает однобайтовые разделители. Но мы не будем утверждать, что тестировал это для всего разнообразия платформ * nix. Поэтому, если мы что-то пропустим, не стесняйтесь использовать раздел комментариев, чтобы исправить нас!
По историческим причинам:
Команды:
paste -d «\ 0″… paste -d «»…
не обязательно эквивалентны; последний не указан этим томом IEEE Std 1003.1-2001 и может привести к ошибке. Конструкция «\0» используется для обозначения «без разделителя», поскольку исторические версии past не соответствовали синтаксическим рекомендациям и команде:
paste -d «»…
getopt () не может быть обработан должным образом.
Таким образом, переносимый способ вставки файлов без использования разделителя — это указать разделитель \0. Это несколько противоречит здравому смыслу, поскольку для многих команд, \0 означает символ NUL -a символ, закодированный как байты из одних нулей, которые не должно конфликтовать с любым текстовым содержанием.
Вы можете найти символ NUL полезным разделителем, особенно когда ваши данные могут содержать произвольные символы (например, при работе с именами файлов или данными, предоставленными пользователем). К сожалению, мы не знаем, как использовать символ NUL в качестве разделителя полей с помощью команды paste. Но, может быть, вы знаете, как это сделать? Если это так, мы хотели бы прочитать ваше решение в разделе команд.
С другой стороны, pasteчасть реализации GNU Coreutils имеет нестандартную -zопцию для перехода от новой строки к символу NUL для разделителя строк. Но в этом случае символ NUL будет использоваться как разделитель строк как для ввода, так и для вывода. Итак, чтобы проверить эту функцию, нам нужна первая версия наших входных файлов с нулевым завершением:
sh$ tr '\n' '\0' < ACCOUNTLIB.csv > ACCOUNTLIB.zero sh$ tr '\n' '\0' < ACCOUNTNUM.csv > ACCOUNTNUM.zero
Чтобы узнать, что изменилось в процессе, мы можем использовать эту утилиту hexdump для изучения исходного двоичного содержимого файлов:
sh$ hexdump -C ACCOUNTLIB.csv | head -5 00000000 41 43 43 4f 55 4e 54 4c 49 42 0a 54 49 44 45 20 |ACCOUNTLIB.TIDE | 00000010 53 43 48 45 44 55 4c 45 0a 56 41 54 20 42 53 2f |SCHEDULE.VAT BS/| 00000020 45 4e 43 0a 50 41 59 41 42 4c 45 53 0a 41 43 43 |ENC.PAYABLES.ACC| 00000030 4f 4d 4f 44 41 54 49 4f 4e 20 47 55 49 44 45 0a |OMODATION GUIDE.| 00000040 56 41 54 20 42 53 2f 45 4e 43 0a 50 41 59 41 42 |VAT BS/ENC.PAYAB| sh$ hexdump -C ACCOUNTLIB.zero | head -5 00000000 41 43 43 4f 55 4e 54 4c 49 42 00 54 49 44 45 20 |ACCOUNTLIB.TIDE | 00000010 53 43 48 45 44 55 4c 45 00 56 41 54 20 42 53 2f |SCHEDULE.VAT BS/| 00000020 45 4e 43 00 50 41 59 41 42 4c 45 53 00 41 43 43 |ENC.PAYABLES.ACC| 00000030 4f 4d 4f 44 41 54 49 4f 4e 20 47 55 49 44 45 00 |OMODATION GUIDE.| 00000040 56 41 54 20 42 53 2f 45 4e 43 00 50 41 59 41 42 |VAT BS/ENC.PAYAB|
Мы дам вам сравнить два гексагональных дампа, чтобы определить разницу между «.zero» файлами и исходными текстовыми файлами. В качестве подсказки я могу сказать, что новая строка кодируется как 0a байт.
Надеемся, вы нашли время, необходимое для поиска символа NUL в входных файлах «.zero». Во всяком случае, у нас есть версия входных файлов с нулевым завершением, поэтому мы можем использовать -zопцию pasteкоманды для обработки этих данных, создавая на выходе и результат с нулевым завершением:
# Подсказка: в шестнадцатеричном дампе: # байт 00 является символом NUL # байт 09-символ табуляции # Посмотрите на любую таблицу ASCII, чтобы найти отображение # для букв или других символов # (https://en.wikipedia.org/wiki/ASCII#Character_set) sh$ paste -z *.zero | hexdump -C | head -5 00000000 41 43 43 4f 55 4e 54 4c 49 42 09 41 43 43 4f 55 |ACCOUNTLIB.ACCOU| 00000010 4e 54 4e 55 4d 00 54 49 44 45 20 53 43 48 45 44 |NTNUM.TIDE SCHED| 00000020 55 4c 45 09 36 32 33 34 37 37 00 56 41 54 20 42 |ULE.623477.VAT B| 00000030 53 2f 45 4e 43 09 34 34 35 34 35 32 00 50 41 59 |S/ENC.445452.PAY| 00000040 41 42 4c 45 53 09 34 33 35 36 00 41 43 43 4f 4d |ABLES.4356.ACCOM| # Используя утилиту 'tr', мы можем отобразить \0 на новую строку # для отображения выходных данных на консоли: sh$ paste -z *.zero | tr '\0' '\n' | head -3 ACCOUNTLIB ACCOUNTNUM TIDE SCHEDULE 623477 VAT BS/ENC 445452
Поскольку мои входные файлы не содержат встроенных строк в данных, этот -zпараметр имеет ограниченную полезность здесь. Но на основе приведенных выше объяснений я позволяю вам понять, почему следующий пример работает «как ожидалось». Чтобы полностью понять, что вам, вероятно, нужно загрузить образцы файлов и изучить их на уровне байта, используя hexdumpутилиту, как мы это делали выше:
# Каким-то образом утилита head запуталась # по содержанию файла учетных записей (интересно, почему?;) sh$ head -3 CATEGORIES ACCOUNTS ==> CATEGORIES <== PRIVATE ACCOMMODATION GUIDE SHARED ==> ACCOUNTS <== 6233726230846265106159126579914356613866618193623477623795445333445452605751
# Вывод вполне удовлетворительный, ставлю номер счета # после имени учетной записи и сохранения удивительно красиво отформатированных вещей: sh$ paste -z -d':' CATEGORIES ACCOUNTS | tr '\0' '\n' | head -5 PRIVATE ACCOMMODATION GUIDE:623372 SHARED ADVERTISEMENTS:623084
Команда paste выводит только выделенный текст. Но, если ваша система поддерживает BSD- утилиту column, вы можете использовать ее для получения красиво отформатированных таблиц путем преобразования pasteвывода команды в текстовый формат с фиксированной шириной. Но это будет предметом предстоящей статьи. Так что следите за обновлениями и, как всегда, не забудьте поделиться этой статьей на своих любимых сайтах и в социальных сетях!