Как преобразовать данные, разделенные табуляцией, в данные, разделенные запятыми?

Вопрос или проблема

Я запрашиваю список снимков EC2 с помощью инструмента командной строки EC2 от Amazon:

ec2-describe-snapshots -H --hide-tags > snapshots.csv

Данные выглядят примерно так:

SnapshotId      VolumeId        StartTime   OwnerId         VolumeSize  Description
snap-00b66464   vol-b99a38d0    2012-01-05  5098939         160         my backup

Как я могу перехватить данные перед перенаправлением в snapshots.csv и выполнить следующие действия:

  • заменить “табуляции” на запятые
  • обернуть значения в кавычки
  • если значение состоит только из цифр, добавьте к нему префикс =, чтобы Excel воспринимал его как текст — например, OwnerId должен быть "=5098939” (этот пункт не обязателен, если его нельзя выполнить напрямую и потребуется вместо этого файл скрипта или функция)

желаемый вывод:

"SnapshotId","VolumeId","StartTime","OwnerId","VolumeSize","Description"
"snap-00b66464","vol-b99a38d0","2012-01-05","=5098939","=160","my backup"

#!/usr/bin/awk -f

BEGIN { FS = "\t"; OFS = "," }
{
    for(i = 1; i <= NF; i++) {
        if ($i + 0 == $i) { $i = "=" $i }
        else gsub(/"/, "\"\"", $i);
        $i = "\"" $i "\""
    }
    print
}

Предположим, что вы назовете это convert.awk, вы можете вызвать его следующим образом:

ec2-describe-snapshots -H --hide-tags | awk -f convert.awk > snapshots.csv

или (после добавления прав на выполнение, chmod a+x convert.awk)

ec2-describe-snapshots -H --hide-tags | ./convert.awk > snapshots.csv

Это создаст новую колонку для каждой табуляции, что сохранит колонку комментариев вместе (если она не содержит табуляций), но добавит пустые колонки (так выглядит ваш пример вывода, так что, возможно, вы именно это и хотите).
Если вы хотите разбивать по всем пробелам (это уберет лишние табуляции внутри таблицы, но сделает каждое слово в описании новой колонкой), уберите оператор FS="\t";.

Для будущих поколений, если вам не нужны ", = или встроенные пробелы, вы можете сделать его однострочником:

awk -v OFS=, '{$1=$1;print}'

Вот решение на Perl. Это может быть возможно с помощью sed/awk, но проверка на число, скорее всего, сделает его довольно тяжелым.

ec2-describe-snapshots -H --hide-tags | \
perl -e 'use Scalar::Util qw(looks_like_number);
         while (chomp($line = <STDIN>)) {
             print(join(",", map { "\"" . (looks_like_number($_) ? "=$_" :
                                           do {s/"https://unix.stackexchange.com/""/g; $_}) . "\"" }
             split(/\t/, $line)) . "\n");
         }' \
> snapshots.csv

Если вам просто лень, как и мне, и вы хотите сделать все одной командой без написания скрипта, вот как я бы это сделал.

ec2-describe-snapshots -H --hide-tags | sed -e 's/^I/","/g' | sed -e 's/^/"https://unix.stackexchange.com/" | sed -e 's/$/"https://unix.stackexchange.com/"> snapshots.csv

^I создается нажатием ctrl+v i.

Первая sed меняет все табуляции на ",". Вторая sed вставляет " в начало каждой строки, а последняя sed вставляет закрывающую " в конце каждой строки.

Еще одно решение на Perl:

#!/usr/bin/perl -wln
use strict;

my($n,$s);chomp();
for $s ( split(/\t/,$_) )
{
    $s="=".$s if ($s =~ /^\d+$/);
    $n.= '"'.$s.'",';
}
$n =~ s/(.*),/$1/;print $n;

вызовите с помощью ec2-describe-snapshots -H --hide-tags | /var/tmp/script.pl > output.txt

sed — это наиболее полезная утилита в Linux, которую я когда-либо встречал.

sed 's/\t/","/g' TabSeparatedValues.txt > CommaSeparatedValues.csv
sed -i 's/.*/"&"https://unix.stackexchange.com/" CommaSeparatedValues.csv

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

Это может вам помочь:

sed 's/\t\+/,/g;s/^\|$/"/g;s/,/"&"/g;s/"\([0-9]\+\)"https://unix.stackexchange.com/"=\1"/g' file

Сначала добавьте символ = в начало каждого числового поля и преобразуйте в CSV с помощью Miller (mlr):

$ mlr --t2c put 'for (k,v in $*) { is_numeric(v) { $[k] = "=" . v } }' file
SnapshotId,VolumeId,StartTime,OwnerId,VolumeSize,Description
snap-00b66464,vol-b99a38d0,2012-01-05,=5098939,=160,my backup

Поскольку Miller никогда не обрамляет поля в кавычки, если это не необходимо, мы используем csvformat из пакета csvkit для повторного форматирования вывода CSV от Miller с полностью заключенными в кавычки полями:

$ mlr --t2c put 'for (k,v in $*) { is_numeric(v) { $[k] = "=" . v } }' file | csvformat -U2
"SnapshotId","VolumeId","StartTime","OwnerId","VolumeSize","Description"
"snap-00b66464","vol-b99a38d0","2012-01-05","=5098939","=160","my backup"

Ответ или решение

Преобразование данных из табличного формата с разделением значений табуляцией в формат CSV, с разделением запятыми, может оказаться полезным во многих сценариях, например, для импорта данных в электронные таблицы или другие системы, которые работают с форматом CSV. Ваша задача заключается в том, чтобы взять вывод команды ec2-describe-snapshots и преобразовать его в нужный формат, например, на момент вывода данных о снимках EC2 из командной строки AWS.

Теория

Для начала рассмотрим, что именно требуется для выполнения поставленной задачи. Ваша цель состоит в трех основных операциях с данными:

  1. Замена табуляций на запятые: Поскольку табуляции используются для разделения полей в выводе команды, их замена на запятые преобразует вывод в формат CSV.

  2. Обрамление значений кавычками: Это необходимо для корректной интерпретации данных, особенно когда значения содержат запятые или другие специальные символы.

  3. Добавление префикса "=" числовым значениям: Это поможет Excel интерпретировать данные как текст, а не как числа, тем самым предотвращая вероятные нежелательные преобразования данных (например, изменение формата даты или сокращение длинных номеров).

Пример

Рассмотрим пример вывода, который вы получаете:

SnapshotId      VolumeId        StartTime   OwnerId         VolumeSize  Description
snap-00b66464   vol-b99a38d0    2012-01-05  5098939         160         my backup

Вам нужно преобразовать этот вывод в следующее:

"SnapshotId","VolumeId","StartTime","OwnerId","VolumeSize","Description"
"snap-00b66464","vol-b99a38d0","2012-01-05","=5098939","=160","my backup"

Применение

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

Подход с использованием AWK

AWK — это мощный инструмент для обработки текстовых данных в Unix-подобных системах. С помощью небольшой AWK-программы мы можем выполнить все нужные преобразования:

#!/usr/bin/awk -f

BEGIN { FS = "\t"; OFS = "," }
{
    for(i = 1; i <= NF; i++) {
        if ($i + 0 == $i) { $i = "=" $i }
        else gsub(/"/, "\"\"", $i);
        $i = "\"" $i "\""
    }
    print
}

Этот AWK-скрипт делает следующее:

  • Устанавливает табуляцию как разделитель входных данных (FS = "\t").
  • В цикле проходит по каждому полю и обрабатывает его:
    • Если значение числовое, добавляет знак равенства перед ним.
    • Заменяет двойные кавычки в тексте на две двойные кавычки для правильного вывода в CSV формате.
    • Обрамляет каждое значение кавычками.
  • Выводит строку, преобразуя все поля в соответствии с требованиями.

AWK-скрипт можно исполнять следующими методами:

ec2-describe-snapshots -H --hide-tags | awk -f convert.awk > snapshots.csv

Подход с использованием Perl

Perl предоставляет мощные средства для работы со строками и является хорошим выбором для подобных задач:

ec2-describe-snapshots -H --hide-tags | \
perl -e 'use Scalar::Util qw(looks_like_number);
         while (chomp($line = <STDIN>)) {
             print(join(",", map { "\"" . (looks_like_number($_) ? "=$_" :
                                           do {s/\"/""/g; $_}) . "\"" }
             split(/\t/, $line)) . "\n");
         }' \
> snapshots.csv

В этом скрипте:

  • Используется модуль Scalar::Util для проверки числовых значений,
  • Входные строки разделяются по табуляции,
  • Для каждого значения осуществляется проверка: если оно числовое, добавляется знак равенства; если нет, оно обрамляется кавычками с учетом замены внутренних кавычек.

Sed и другие подходы

Для быстрого решения задачи можно также использовать sed, хотя это может быть менее гибким способом:

ec2-describe-snapshots -H --hide-tags | sed -e 's/\t/","/g' | sed -e 's/^/"/' | sed -e 's/$/"/' > snapshots.csv
  • Первый sed заменяет табуляции на ",".
  • Второй sed добавляет начальную кавычку.
  • Третий sed добавляет конечную кавычку.

Заключение

Существуют различные подходы для преобразования данных из одного формата в другой. В зависимости от ваших требований и предпочитаемой технологии, вы можете выбрать наиболее подходящий способ. AWK и Perl предоставляют гибкие инструменты для обработки строк, в то время как sed подходит для простых однотипных замен. Независимо от выбора, результатом будет CSV-файл, удобный для дальнейшего использования.

Оцените материал
Добавить комментарий

Капча загружается...