Вопрос или проблема
из скрипта ниже:
EmpID:Name:Designation:UnitName:Location:DateofJoining:Salary
1001:Thomson:SE:IVS:Mumbai:10-Feb-1999:60000
1002:Johnson:TE::Bangalore:18-Jun-2000:50000
1003:Jackson:DM:IMS:Hyderabad:23-Apr-1985:90000
1004:BobGL::ETA:Mumbai:05-Jan-2004:55000
1005:Alice:PA:::26-Aug-2014:25000
1006:LilySE::IVS:Bangalore:17-Dec-2015:40000
1007:Kirsten:PM:IMS:Mumbai:26-Aug-2014:45000
1004:BobGL::ETA:Mumbai:05-Jan-2021:55000
Я хотел бы получить количество пустых мест (которые представлены как ‘::’). Большое спасибо за вашу поддержку.
Предполагая, что вы хотите подсчитать количество пустых полей в вашем файле CSV, разделенном двоеточием, мы можем сделать это с помощью Miller (mlr
) следующим образом:
$ mlr --csv --ifs colon put -q 'for (k,v in $*) { is_empty(v) { @count=@count+1 } } end { print @count }' file
6
Поскольку Miller понимает CSV, он также правильно обрабатывает поля, содержащие встроенные двоеточия. Например, следующий CSV имеет два пустых поля (Designation
и Salary
):
EmpID:Name:Designation:UnitName:Location:DateofJoining:Salary
1008:"Text::CSV"::"Team::Overseas":Stockholm:2025-03-03:
Вы можете объединить несколько стандартных утилит:
редактировать: Спасибо @Kusalananda за напоминание о grep -c
<file.txt tr ':' '\n' | grep -c '^$'
или использовать awk
:
<file.txt awk -F: '{for (i=1; i<=NF; i++) n+=($i=="")} END {print n}'
То есть до тех пор, пока входные поля не содержат символов LF или :.
Вам нужно чистое Bash решение?
пример на awk, который POSIX!
awk 'BEGIN { counter=0; } { text=$0; while (spaceI=index(text,"::")) { text=substr(text,spaceI+2); counter++; }} END { print counter; }' < your_file
или
echo - n 'EmpID:Name:Designation:UnitName:Location:DateofJoining:Salary
1001:Thomson:SE:IVS:Mumbai:10-Feb-1999:60000
1002:Johnson:TE::Bangalore:18-Jun-2000:50000
1003:Jackson:DM:IMS:Hyderabad:23-Apr-1985:90000
1004:BobGL::ETA:Mumbai:05-Jan-2004:55000
1005:Alice:PA:::26-Aug-2014:25000
1006:LilySE::IVS:Bangalore:17-Dec-2015:40000
1007:Kirsten:PM:IMS:Mumbai:26-Aug-2014:45000
1004:BobGL::ETA:Mumbai:05-Jan-2021:55000' | awk 'BEGIN { counter=0; } { text=$0; while (spaceI=index(text,"::")) { text=substr(text,spaceI+2); counter++; }} END { print counter; }'
Bash:
{ counter=0; while IFS= read -r line; do doIT=1; while ((doIT > 0)); do line_L=${#line}; line=${line#*::}; if ((line_L > ${#line})); then ((counter++)); else doIT=0; fi; done; done; echo -n "$counter" ; } < your_file
Вот реализация с использованием sed
и wc
, а также чистая реализация на bash
, вместе с образцом кода для вызова обоих. Я бы рекомендовал первое.
#!/bin/bash
sedwc() {
local v=( $( sed <$1 -e 's/^/:/;s/$/:/;s/[^:][^:]*/x/g;s/::/:y:/g;s/::/:y:/g;s/[x:]//g' | wc ) )
echo $(( ${v[2]} - ${v[0]} ))
}
purebash() {
local line count=0 mod
while read line
do
while true
do
mod="${line/::/:}"
[[ "$line" = "$mod" ]] && break
: $(( count++ ))
line="$mod"
done
done <"$1"
echo $count
}
a=$( sedwc sample )
b=$( purebash sample )
echo sedwc = "$a" purebash = "$b"
Одно, что подразумевает ваш формат входных данных, но вы не указываете, это то, что пустые записи могут быть в начале и в конце строки. Реализация sedwc учитывает их, добавляя дополнительное двоеточие в начале и конце каждой строки. Если эти пустые значения не должны учитываться, вы можете удалить текст s/^/:/;s/$/:/;
.
Я считаю, что стоит объяснить реализацию sed wc
:
команда | значение | образец содержимого |
---|---|---|
1005:Alice:PA:::26-Aug-2014:25000 | ||
s/^/:/; | добавить дополнительное двоеточие в начале, чтобы поймать пустое первое поле | :1005:Alice:PA:::26-Aug-2014:25000 |
s/$/:/; | добавить дополнительное двоеточие в конце, чтобы поймать пустое последнее поле | :1005:Alice:PA:::26-Aug-2014:25000: |
s/[^:][^:]*/x/g; | заменить все непустые поля на “x” | :x:x:x:::x:x: |
s/::/:y:/g; | заменить каждое второе пустое поле на “y” | :x:x:x:y::x:x: |
s/::/:y:/g; | заменить другие пустые поля на “y” | :x:x:x:y:y:x:x: |
s/[x:]//g | удалить все “x” и “:” значения, оставив только “y”. | yy |
Затем wc
выводит всего строки, всего слова и всего символы. Это переводится как: всего записи, записи с любыми пустыми полями и всего записи плюс всего пустые поля. Некоторое простое уравнение затем выводит количество пустых полей.
Используя Raku (ранее известный как Perl_6)
~$ BEGIN my $i; m:overlap/ [ ^ ":" || "::" || ":" $ ] { ++$i } /; END say $i;' file
Raku — это язык программирования в семействе Perl, который является хорошим выбором, если вы работаете с файлом Unicode/UTF-8.
Выше мы вызываем Raku в командной строке с флагами -ne
, аналогичными awk, но без автопечати. Мы BEGIN
объявляем переменную-счётчик $i
. Чтобы обеспечить корректное подсчёте смежных двоеточий, мы используем модификатор :overlap
(именованный аргумент).
Пример входных данных:
EmpID:Name:Designation:UnitName:Location:DateofJoining:Salary
1001:Thomson:SE:IVS:Mumbai:10-Feb-1999:60000
1002:Johnson:TE::Bangalore:18-Jun-2000:50000
1003:Jackson:DM:IMS:Hyderabad:23-Apr-1985:90000
1004:BobGL::ETA:Mumbai:05-Jan-2004:55000
1005:Alice:PA:::26-Aug-2014:25000
1006:LilySE::IVS:Bangalore:17-Dec-2015:40000
1007:Kirsten:PM:IMS:Mumbai:26-Aug-2014:45000
1004:BobGL::ETA:Mumbai:05-Jan-2021:55000
Пример выхода: 6
Если вы хотите увидеть совпадения, просто добавьте вызов say
, с ++$
и =>
для предоставления номеров строк:
~$ raku -ne 'BEGIN my $i; say ++$, " => ", m:overlap/ [ ^ ":" || "::" || ":" $ ] { ++$i } /; END say "total blank fields: $i";' file
1 => ()
2 => ()
3 => (「::」)
4 => ()
5 => (「::」)
6 => (「::」 「::」)
7 => (「::」)
8 => ()
9 => (「::」)
total blank fields: 6
Ответ или решение
Для подсчёта пустых полей в файле с разделителями в Unix можно использовать различные инструменты и подходы. Ваша задача заключается в нахождении количества полей, которые являются пустыми, то есть выражены в виде двух двоеточий (::) подряд. Прежде чем углубиться в процесс решения этой задачи, давайте обсудим теоретические основы, приведем примеры и способы, как это применить на практике.
Теория
Файлы с разделителями, такие как CSV (Comma Separated Values), часто используются для хранения табличных данных. В вашем случае, данный файл использует двоеточие (:) в качестве разделителя. Задача состоит в определении, сколько полей в каждой строке файла пусты, т.е. заключены между двумя двоеточиями ::.
Пустые поля могут возникать по нескольким причинам: отсутствие данных в процессе записи, намеренный пропуск информации и т.д. Понимание того, как обработать и подсчитать такие поля, важно для анализа полноты данных и дальнейшего анализа информации.
Пример
У вас имеется файл с такими данными:
EmpID:Name:Designation:UnitName:Location:DateofJoining:Salary
1001:Thomson:SE:IVS:Mumbai:10-Feb-1999:60000
1002:Johnson:TE::Bangalore:18-Jun-2000:50000
1003:Jackson:DM:IMS:Hyderabad:23-Apr-1985:90000
1004:BobGL::ETA:Mumbai:05-Jan-2004:55000
1005:Alice:PA:::26-Aug-2014:25000
1006:LilySE::IVS:Bangalore:17-Dec-2015:40000
В этом примере строка 3 содержит 1 пустое поле, строка 5 содержит 2 пустых поля.
Применение
В UNIX среде можно использовать различные инструменты для вскрытия, обработки и анализа таких файлов. Рассмотрим несколько подходов.
Использование awk
awk
— мощный текстовый процессор, который можно использовать для этого типа задач. Вот пример команды для подсчета пустых полей в файле с разделителями:
awk -F: '{for (i=1; i<=NF; i++) n+=($i=="")} END {print n}' < ваш_файл
Эта команда устанавливает двоеточие как разделитель и проходит по каждому полю, увеличивая счетчик, если поле пустое.
Использование sed и wc
sed
в сочетании с wc
предоставляет другой подход обработки:
sed -e 's/^/:/;s/$/:/;s/[^:][^:]*/x/g;s/::/:y:/g;s/::/:y:/g;s/[x:]//g' < ваш_файл | wc -c
Этот метод заменяет каждый элемент данных на x
, отмечает пустые поля y
, а затем подсчитывает количество y
.
Использование bash
Более "ручной" метод с использованием встроенного bash:
{ counter=0; while IFS= read -r line; do while [[ "$line" == *::* ]]; do line="${line/::/:}"; ((counter++)); done; done; echo "$counter"; } < ваш_файл
Этот цикл обрабатывает строку за строкой контент файла и использует условные операции bash для подсчета пустых полей.
Использование Miller
Miller (mlr
) — утилита для обработки данных в формате CSV или аналогичных. Команда для этой задачи может выглядеть так:
mlr --csv --ifs colon put -q 'for (k,v in $*) { if (v == "") { @count+=1 } } end { print @count }' файл
Miller отлично справляется с CSV-файлами, учитывая экранированные символы и сложные структуры данных.
Заключение
Выбор инструмента и метода зависит от специфики задачи, объема данных и личных предпочтений. Каждый из представленных методов способен эффективно подсчитать пустые поля в файле с двоеточием в качестве разделителя, но при этом предоставляет свою гибкость в зависимости от контекста. Важно помнить о возможных подводных камнях, таких как строки с вложенными разделителями или не злонамеренно сформированные файлы.