Вопрос или проблема
У меня есть файл, подобный этому
103710:v2HAbAFH029324:[email protected]:localhost:Отправлено
103821:CCFE5609E3:[email protected]:localhost:Возврат
103922:DFF19609E2:[email protected]:localhost:Отложено
Мне нужно изменить это на
{"randomId":{"s":"103710"},"id":{"s":"v2HAbAFH029324"},"userId":{"s":"[email protected]"},"dns":{"s":"localhost"},"status":{"s":"Отправлено"}}
{"randomId":{"s":"103821"},"id":{"s":"CCFE5609E3"},"userId":{"s":"[email protected]"},"dns":{"s":"localhost"},"status":{"s":"возврат"}}
{"randomId":{"s":"103922"},"id":{"s":"DFF19609E2"},"userId":{"s":"[email protected]"},"dns":{"s":"localhost"},"status":{"s":"Отложено"}}
Я думаю, что код будет выглядеть так
while read line
do
sed -i 's/^/{"randomId":{"s":"https://unix.stackexchange.com/" test
echo $line
echo $line | grep -q ":"
[ $? -eq 0 ] && echo "https://unix.stackexchange.com/"{"id":{"s":/"
[ $? -eq 1 ] && echo "https://unix.stackexchange.com/",{"userId":{"s":/"
[ $? -eq 2 ] && echo "https://unix.stackexchange.com/",{"host":{"s":/"
[ $? -eq 3 ] && echo "https://unix.stackexchange.com/",{"status":{"s":/"
echo "$line | " ";
done < test
Добавление для первого вхождения : {"id":{"s":
и затем второго вхождения добавляя {"userId":{"s":
Этот блок grep/echo не сделает ничего полезного; $? будет установлен один раз — он не будет итерировать по полям.
К счастью, существует гораздо более простой способ сделать это: просто разделите поля на переменные. К счастью, read
может сделать это для вас:
while IFS=':' read -r randomid id userid dns status; do
printf '{"randomId":{"s":"%s"},"id":{"s":"%s"},"userId":{"s":"%s"},"dns":{"s":"%s"},"status":{"s":"%s"}}\n' \
"$randomid" "$id" "$userid" "$dns" "$status"
done
Использование printf
вместо более знакомого echo
избегает всех \"
-последовательностей, которые потребовал бы echo
. Обратите внимание на обратный слеш в конце строки для разделения.
Кстати: Формат, который вы производите, называется JSON, и могут быть инструменты, помогающие его генерировать (например, jq). Также это может потребовать собственного экранирования, если, например, ваши поля могут содержать двойные кавычки.
С помощью perl
:
perl -MJSON -F: -ple '@A = qw/randomId id userId dns status/; $_ = encode_json({map { shift @A => { "s" => $_ } } @F } )' input.csv
Поскольку ваши данные разделены и их легко читать, есть несколько способов сделать это. Sed может разобрать ваши данные за одну строку и вывести изменения:
sed -r -i 's/^(.*):(.*):(.*):(.*):(.*)$/{"randomId":{"s":"\1"},"id":{"s":"\2"},"userId":{"s":"\3"},"dns":{"s":"\4"},"status":{"s":"\5"}}/' input.txt
Вы используете группы захвата, чтобы захватить все между началом файла, вашим разделителем и концом файла, а затем просто манипулируете текстом вокруг этих групп. Каждая группа захвата ссылается на “\#” где # — это номер группы захвата, начиная с одного и увеличивая на один для каждой группы.
Как уже упоминалось, вы также можете установить свой собственный разделитель. Bash имеет встроенную переменную, называемую IFS (внутренний разделитель полей). IFS по умолчанию равен пробелу, но вы можете изменить его. Я не буду показывать пример в bash, так как он уже был дан, и это просто было бы его копией.
perl -F: -pale '
@A = qw/randomId id userId dns status/;
($k, $_) = (0, "{" . join(",", map qq/"$A[$k++]":{"s":"$_"}/, @F) . "}");
' ваш_файл
Объяснение
Массив @F
содержит поля, разделенные на :
, которые затем соединяются с соответствующей обработкой {"s":"полеI"}
и предшествуют соответствующему элементу из массива @A
. Все эти элементы собираются вместе с помощью join
на ,
и заключаются в “{” … “}”. И вы закончили.
Используя Miller (mlr
) для чтения данных как разделенных двоеточиями полей, добавляя требуемый ключ к каждому полю, а затем перемещая значения вниз в под-объекты с ключом s
:
mlr --n2l --ifs colon --jvquoteall \
label randomId,id,userId,dns,status затем \
put 'for (k in $*) { $[k] = { "s": $[k] } }' файл
Здесь опция --n2l
является сокращением для --inidx --ojsonl
, --ifs
устанавливает разделитель, а --jvquoteall
заставляет Miller выводить все значения как строки (числовые данные в противном случае не будут заключены в кавычки). Глагол label
устанавливает имена полей в заданный список по порядку, а выражение put
итерирует по каждому полю и перемещает данные вниз в под-объект с ключом s
.
Вывод:
{"randomId": {"s": "103710"}, "id": {"s": "v2HAbAFH029324"}, "userId": {"s": "[email protected]"}, "dns": {"s": "localhost"}, "status": {"s": "Отправлено"}}
{"randomId": {"s": "103821"}, "id": {"s": "CCFE5609E3"}, "userId": {"s": "[email protected]"}, "dns": {"s": "localhost"}, "status": {"s": "возврат"}}
{"randomId": {"s": "103922"}, "id": {"s": "DFF19609E2"}, "userId": {"s": "[email protected]"}, "dns": {"s": "localhost"}, "status": {"s": "Отложено"}}
Другой подход — использовать jq
для чтения ввода как сырых текстовых строк, разделения каждой строки на массив отдельных строк по двоеточиям, а затем создания отдельных объектов JSON с ключом s
как единственным ключом и строкой как значением этого ключа. Массив объектов затем преобразуется в объект с ключами, взятыми из аргумента командной строки --args
.
jq -R -c 'split(":") | map({ s: . }) | with_entries(.key = $ARGS.positional[.key])' файл --args randomId id userId dns status
Результат:
{"randomId":{"s":"103710"},"id":{"s":"v2HAbAFH029324"},"userId":{"s":"[email protected]"},"dns":{"s":"localhost"},"status":{"s":"Отправлено"}}
{"randomId":{"s":"103821"},"id":{"s":"CCFE5609E3"},"userId":{"s":"[email protected]"},"dns":{"s":"localhost"},"status":{"s":"возврат"}}
{"randomId":{"s":"103922"},"id":{"s":"DFF19609E2"},"userId":{"s":"[email protected]"},"dns":{"s":"localhost"},"status":{"s":"Отложено"}}
Вы можете найти csvjson
полезным. Пожалуйста, обратитесь к документации.
Пример команды
cat data.csv | csvjson -d :
Вывод
[{"103710": 103821.0, "v2HAbAFH029324": "CCFE5609E3", "[email protected]": "[email protected]", "localhost": "localhost", "Sent": "возврат"}, {"103710": 103922.0, "v2HAbAFH029324": "DFF19609E2", "[email protected]": "[email protected]", "localhost": "localhost", "Sent": "Отложено"}]
Установка
pip install csvkit
Пожалуйста, обратитесь к документации для получения актуальной информации.
Ответ или решение
Чтобы преобразовать данные, разделённые двоеточием, в формат JSON с использованием оболочки (shell), можно воспользоваться разными подходами. Ниже представлено несколько способов достижения желаемого результата, включая использование командной строки и языков скриптов, таких как Bash и Perl.
Метод 1: Использование Bash с read
Наиболее простым и понятным методом является использование встроенной функции read
в Bash. Например:
while IFS=':' read -r randomId id userId dns status; do
printf '{"randomId":{"s":"%s"},"id":{"s":"%s"},"userId":{"s":"%s"},"dns":{"s":"%s"},"status":{"s":"%s"}}\n' \
"$randomId" "$id" "$userId" "$dns" "$status"
done < input.txt
- Что происходит в коде?
IFS=':'
устанавливает символ разделителя на двоеточие.read -r
читает каждый элемент строки и присваивает их переменнымrandomId
,id
,userId
,dns
,status
.printf
формирует нужный формат JSON для каждой строки и выводит результат.
Метод 2: Использование команд sed
Вы также можете воспользоваться утилитой sed
для однократного преобразования данных:
sed -r 's/^(.*):(.*):(.*):(.*):(.*)$/{"randomId":{"s":"\1"},"id":{"s":"\2"},"userId":{"s":"\3"},"dns":{"s":"\4"},"status":{"s":"\5"}}/' input.txt
Пояснение:
- Команда
sed
с использованием регулярных выражений (-r
) захватывает каждую часть, разделённую двоеточием, и заменяет всю строку подходящим форматом JSON.
Метод 3: Использование Perl
Если вам известен Perl, вы можете использовать следующий код:
perl -MJSON -F: -ple '@A = qw/randomId id userId dns status/; $_ = encode_json({map { shift @A => { "s" => $_ } } @F } )' input.txt
Суть:
- Этот скрипт разбивает входные данные на части и использует модуль JSON для преобразования в формат JSON.
Метод 4: Использование jq
Инструмент jq
предназначен для работы с JSON и может быть использован для достижения аналогичных результатов. Использование jq
может показаться избыточным, однако оно предоставляет мощные инструменты для манипуляции с JSON:
jq -Rn '[(inputs | split(":")) | {"randomId": {"s": .[0]}, "id": {"s": .[1]}, "userId": {"s": .[2]}, "dns": {"s": .[3]}, "status": {"s": .[4]}}]' input.txt
Заключение
Каждый из представленных методов имеет свои преимущества в зависимости от контекста задачи и предпочтений разработчика. Bash подходит для простых задач, sed
хорошо справляется с текстовыми заменами, Perl является мощным инструментом для сложных преобразований, а jq
предоставляет гибкость в работе с JSON. Выбор конкретного метода зависит от ваших требований и предпочтений.
Если вы хотите более детально изучить, как каждое решение работает, рекомендую проверять предоставленный код с вашими данными. Также убедитесь, что инструкции соответствуют вашему окружению, так как в разных системах могут потребоваться разные зависимости.