
gdriveимеет подкоманду list
, которая выводит список файлов, как в следующем примере:
gdrive list
Выход:
Id Name Type Size Created
1sV3_a1ySV0-jbLxhA8NIEts1KU_aWa-5 info.pdf bin 10.0 B 2018-08-27 20:26:20
1h-j3B5OLryp6HkeyTsd9PJaAtKK_GYyl 2018-12-ss-scalettapass dir 2018-08-27 20:26:19
Я пытаюсь проанализировать этот вывод с помощью таких инструментов, как awk
и , но sed
безуспешно.
Проблемы заключаются в пустых «полях» в столбце размера и динамической ширине столбцов.
Есть ли у кого-нибудь идеи, как проанализировать этот вывод?
решение1
awk может работать с данными фиксированной ширины. Сначала нам нужно определить ширину столбцов:
fieldwidths=$(head -n 1 file | grep -Po '\S+\s*' | awk '{printf "%d ", length($0)}')
Это значение "36 26 7 9 7 "
-- последнее поле больше 7 символов. Давайте произвольно сделаем его 70 символами:
fieldwidths=${fieldwidths/% /0}
Теперь давайте прочитаем данные и преобразуем их в CSV:
awk -v FIELDWIDTHS="$fieldwidths" '{
for (i=1; i<=NF; i++) {
val = $i
sub(/ *$/, "", val)
gsub(/"/, "\"\"", val)
printf "%s\"%s\"", (i==1 ? "" : ","), val
}
print ""
}' file
выходы:
"Id","Name","Type","Size","Created"
"1sV3_a1ySV0-jbLxhA8NIEts1KU_aWa-5","info.pdf","bin","10.0 B","2018-08-27 20:26:20"
"1h-j3B5OLryp6HkeyTsd9PJaAtKK_GYyl","2018-12-ss-scalettapass","dir","","2018-08-27 20:26:19"
Та же функциональность с perl
perl -lne '
if ($. == 1) {
@head = ( /(\S+\s*)/g );
pop @head;
$patt = "^";
$patt .= "(.{" . length($_) . "})" for @head;
$patt .= "(.*)\$";
}
print join ",", map {s/"/""/g; s/\s+$//; qq("$_")} (/$patt/o);
' file
решение2
Это можно сделать с Perl
помощью unpack
функции, создав шаблон распаковки динамически, проверив заголовок (1-я строка):
perl -lpe '
$fmt //= join "", map("A" . length(), /\H+\h+(?=\H)/g), "A*";
$_ = join ",", map { s/"/""/gr =~ s/(.*)/"$1"/r } unpack $fmt;
' input-file.txt
Объяснение:
-p
заставитperl
потреблять файл построчно. Каждая строка, также известная как запись, называется$_
. Другим эффектом-p
является автоматическая печать текущей записи перед тем, как перейти к извлечению следующей.-l
делает 2 вещи, устанавливаетORS = RS = \n
- Регулярное выражение
/\H+\h+(?=\H)/g
извлекает все поля, кроме последнего, а затем передает их вmap
. map
вычисляет длину этих полей и добавляет к каждому из них префикс «A».- Вместо того, чтобы не выбирать последнее поле выше, мы добавляем универсальное поле «A*».
- Затем они передаются в
join
, который склеивает их в строку с использованием нулевого разделителя. Таким образом, формат распаковки готов к использованию и не вычисляется снова из-за оператора,//=
который являетсяdefined-or
функцией. - Теперь, вооружившись динамически созданным форматом распаковки, мы приступаем к его применению к каждой строке, включая заголовок.
unpack
распаковывает строку, в нашем случае текущую строку, используя предоставленный формат и выводит распакованные поля.- Эти испускаемые поля затем являются входными данными,
map
которые работают с каждым по одному и выполняют шаги, описанные в{ ... }
коде. В нашем случае в каждом поле мы делаем следующее: a) удваиваем двойные кавычки. b) заключаем поле в двойные кавычки. - После
map
завершения редактирования полей он переносит их вjoin
, который объединяет их с помощью запятой,,
формируя небольшой симпатичныйCSV
файл. - P.S.:Обратите внимание, что нам не пришлось обрезать конечные пробелы в полях, сгенерированных
unpack
, так как ,unpack
делает это за вас при использованииA
символа форматирования (A для ASCII).
Выход:
"Id","Name","Type","Size","Created"
"1sV3_a1ySV0-jbLxhA8NIEts1KU_aWa-5","info.pdf","bin","10.0 B","2018-08-27 20:26:20"
"1h-j3B5OLryp6HkeyTsd9PJaAtKK_GYyl","2018-12-ss-scalettapass","dir","","2018-08-27 20:26:19"
Это можно сделать с помощью sed
инструмента, но для этого потребуется двухпроходный подход: сначала, используя строку заголовка входных данных, мы sed
динамически генерируем скрипт, который затем обрабатывает входной файл (включая заголовок) для выполнения требуемой операции, как показано ниже:
if="input-file.txt"
cmd=$(< "$if" head -n 1 | perl -lne 'print join $/, reverse map { $s += length();qq[s/./\\n/$s] } /\H+\h+(?=\H)/g')
sed -e '
'"${cmd}"'
s/"/""/g
s/[[:blank:]]*\n/","/g
s/.*/"&"/
' < "$if"