for filename in *.cluster do class=$(cut -d$'\t' -f1) # Part 1 if [ $(wc -l "$filename") -eq 2 ] # Part 2, start then class=1 fi # Part 2, end printf '%s\n' "$filename" >> class_"$class".txt # Part 3 done
Он состоит из трех частей:
По умолчанию он классифицирует файл на основе первого поля единственной строки. :переменная
class
устанавливается на все, что есть в файле, вплоть до первого символа табуляции в каждой строке. Это будет либо2
, либо3
для класса 2 и 3, поскольку эти файлы имеют только одну строку.
cut
разделяет файлы по разделителям ,$'\t'
— это способ написания символа табуляции в Bash , а-f1
запрашиваетcut
вывод только первого поля с разделителями.- Если в файле две строки (
$(wc -l "$filename") -eq 2
), он должен относиться к классу 1, поэтому переменнаяclass
принудительно устанавливается в 1, заменяя ее значение из шага 1.if
...fi
имеет дело с этим.- Наконец, имя файла добавляется к соответствующему файлу класса:
printf '%s\n' "$filename" >> class_"$class".txt
В конце у вас будет три файла
class_N.txt
для каждого N в 1, 2, 3, с одним именем файла в строке. Если какой-либо файл имеет другое содержимое, отличное от того, что вы указали в вопросе, например, другое первое поле или длину, вы получите дополнительные файлы классов.В неправильном случае, когда имя файла само содержит символ новой строки,это развалится (и даст вам возможность пересмотреть свой выбор имени файла ), но в остальном все должно быть в порядке.
Ваш файл может содержать поле LF в середине -с окончаниями строки CRLF, например. если он был экспортирован из MS -Excel. В этом случае все, что вам нужно с gawk, это:
awk 'BEGIN{RS=ORS="\r\n"; FPAT="[^,]*|(\"[^\"]*\")+"} $1=="a"' file
Например (использование cat -v
только для того, чтобы сделать CR видимыми как ^M
s):
$ cat -v file
head1,head2,head3^M
a,b,c^M
b,no a in first field,c^M
a,"with quotes",c^M
a,"with,",c^M
b,a,1^M
a,"with
newline",c^M
b,1,a^M
$ awk 'BEGIN{RS=ORS="\r\n"; FPAT="[^,]*|(\"[^\"]*\")+"} $1=="a"' file | cat -v
a,b,c^M
a,"with quotes",c^M
a,"with,",c^M
a,"with
newline",c^M
Если по какой-либо причине описанное выше не работает, см.https://stackoverflow.com/questions/45420535/whats-the-most-robust-way-to-efficiently-parse-csv-using-awkили загрузите/используйте расширение парсера gawks CSV в gawkextlib.
смешанный подход sed awk:
$ < file \
sed -e '
s/$/\x00/
s/\(,"[^,"]*\)\x00/\1/
H;1h;$!d;g
s/\x00\n/\x00/g
' |
awk '/^a,/' RS="\x00" -
Комментарии :смешанный sed+awk Я взял ваш код и немного подправил его, чтобы получить желаемые результаты. Основная идея состоит в том, чтобы убрать новые строки, которые неизменно ставит sed. Поэтому мы воздерживаемся от печати sed после обработки каждой записи. Затем в начале мы удаляем новые строки и передаем эти данные с разделителями NUL в awk с NUL в качестве разделителя записей. Затем мы просто ищем записи, начинающиеся с a,
Выход:
a,b,c
a,"with quotes",c
a,"with,",c
a,"with
newline",c
Ниже приведены только методы awk и только sed. Они полагаются на двойную цитату внутри поля в кавычках.
чистый подход sed:
$ sed -Ee ':a
/^(([^"]*"){2})*[^"]*$/!{
$d;N;ba
}
/^a,/!d
' file
чистый awk-подход
$ awk -F\" '
!(NF%2){
t = $0; n = NF
while (getline a > 0) {
t = t ORS a
n = n + split(a, _x, FS)
if (!(nf%2)) break
}
$0 = t
}/^a,/
' file