У меня есть файл CSV, который выглядит так
AS2345, ASDF1232, Mr. Plain Example, 110 Binary ave., Атлантида, Род-Айленд, 12345, (999) 123-5555, 1,56 AS2345, ASDF1232, Mrs. Plain Example, 1121110 Ternary st. 110 Бинарный просп., Атлантида, РИ, 12345, (999) 123-5555, 1,56 AS2345, ASDF1232, Mr. Plain Example, 110 Binary ave., Liberty City, RI, 12345, (999) 123-5555, 1,56 AS2345, ASDF1232, Mr. Plain Example, 110 Ternary ave., Some City, RI, 12345, (999) 123-5555, 1,56
Мне нужно отсортировать его по длине строки, включая пробелы. Следующая команда не содержит пробелов, есть ли способ изменить ее, чтобы она работала для меня?
cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'
Ответы:
Ответ
cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-
Или, чтобы выполнить исходную (возможно, непреднамеренную) подсортировку любых строк одинаковой длины:
cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2-
В обоих случаях мы решили указанную вами проблему, отказавшись от awk для окончательной версии.
Линии одинаковой длины - что делать в случае галстука:
В вопросе не указывалось, требуется ли дальнейшая сортировка для строк одинаковой длины. Я предположил, что это нежелательно, и предложил использовать
-s
(--stable
), чтобы предотвратить сортировку таких строк друг против друга и сохранить их в относительном порядке, в котором они встречаются во входных данных.(Те, кто хочет большего контроля над сортировкой этих связей, могут рассмотреть
--key
вариант сортировки .)Почему попытка решения вопроса не удается (восстановление строки awk):
Интересно отметить разницу между:
echo "hello awk world" | awk '{print}' echo "hello awk world" | awk '{$1="hello"; print}'
Они соответственно уступают
В соответствующем разделе руководства (gawk) упоминается только в стороне, что awk собирается перестроить весь $ 0 (на основе разделителя и т.д.), когда вы изменяете одно поле. Думаю, это не безумное поведение. В нем есть:
«Наконец, бывают случаи, когда удобно заставить awk перестроить всю запись, используя текущее значение полей и OFS. Для этого используйте, казалось бы, безобидное присваивание:»
$1 = $1 # force record to be reconstituted print $0 # or whatever else with $0
«Это заставляет awk восстановить запись».
Тестовый ввод, включающий несколько строк одинаковой длины:
источник
cat $@
он тоже сломан. Вы совершенно определенно хотите процитировать, какcat "$@"
Решение AWK от neillb отлично подходит, если вы действительно хотите его использовать,
awk
и оно объясняет, почему там возникают проблемы , но если вы хотите, чтобы работа была выполнена быстро и не заботится о том, что вы делаете, одно из решений - использоватьsort()
Функция Perl с настраиваемой подпрограммой для перебора входных строк. Вот один лайнер:perl -e 'print sort { length($a) <=> length($b) } <>'
Вы можете поместить это в свой конвейер везде, где вам это нужно, либо получив STDIN (от
cat
или перенаправление оболочки), либо просто передать имя файла perl в качестве другого аргумента и позволить ему открыть файл.В моем случае мне сначала нужны были самые длинные строки, поэтому я поменял местами
$a
и$b
в сравнении.источник
cat testfile.txt | perl -e 'print sort { length($a) <=> length($b) } <>' > out.txt
type testfile.txt | perl -e "print sort { length($a) <=> length($b) } <>" > out.txt
Вместо этого попробуйте эту команду:
awk '{print length, $0}' your-file | sort -n | cut -d " " -f2-
источник
Результаты тестов
Ниже приведены результаты сравнительного анализа решений из других ответов на этот вопрос.
Метод испытания
Полученные результаты
perl
Решение Калеба заняло 11,2 секунды.perl
решение заняло 11,6 секундыawk
раствор # 1 потребовалось 20 секундawk
раствор # 2 занял 23 секундыawk
решение анубхавы заняло 24 секундыawk
Решение Джонатана заняло 25 секундbash
раствор принимает 400x длиннее , чемawk
растворы ( с использованием усеченного тестового примера 100000 строк). Работает нормально, просто занимает вечность.Другое
perl
решениеperl -ne 'push @a, $_; END{ print sort { length $a <=> length $b } @a }' file
источник
Чистый Баш:
declare -a sorted while read line; do if [ -z "${sorted[${#line}]}" ] ; then # does line length already exist? sorted[${#line}]="$line" # element for new length else sorted[${#line}]="${sorted[${#line}]}\n$line" # append to lines with equal length fi done < data.csv for key in ${!sorted[*]}; do # iterate over existing indices echo -e "${sorted[$key]}" # echo lines with equal length done
источник
length()
Функция делает включать пробелы. Я бы внес незначительные изменения в ваш конвейер (включая отказ от UUOC ).awk '{ printf "%d:%s\n", length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]*://'
Команда
sed
напрямую удаляет цифры и двоеточие, добавленныеawk
командой. В качестве альтернативы можно сохранить форматирование отawk
:awk '{ print length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]* //'
источник
Я обнаружил, что эти решения не будут работать, если ваш файл содержит строки, начинающиеся с номера, поскольку они будут отсортированы численно вместе со всеми подсчитанными строками. Решение состоит в том, чтобы дать
sort
в-g
(вообще-числовой сортировки) флаг вместо-n
(числовой сортировки):awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2-
источник
-n
на ваш предложило-g
какие-либо улучшения, поэтому я не ожидаю. В своем ответе я обратился к вопросу о том, как запретить подсортировку строк одинаковой длины (с помощью--stable
). Независимо от того, имели ли вы это в виду или нет, спасибо, что обратил на это мое внимание! Я также добавил продуманный ввод для тестирования.awk
часть будет генерировать список строк с префиксом длины строки и пробелом. Подключение к немуsort -n
будет работать, как ожидалось. Но если какая-либо из этих строк уже имеет номер в начале, эти строки будут начинаться с длины + пробела + числа.sort -n
игнорирует это пространство и будет рассматривать его как одно число, состоящее из длины + числа.-g
Вместо этого использование флага остановится на первом пробеле, что приведет к правильной сортировке. Попробуйте сами, создав файл с несколькими строками с префиксом цифр и пошагово выполните команду.sort -n
не учитывает пространство и производит неправильную сортировку.sort -g
выводит правильный порядок.-n
вsort (GNU coreutils) 8.21
. Вinfo
документации описывается-g
как менее эффективный и потенциально менее точный (он преобразует числа в числа с плавающей запятой), поэтому, вероятно, не используйте его, если вам это не нужно.-n
: "Сортировка в числовом виде. Число начинается с каждой строки и состоит из необязательных пробелов, необязательного знака '-' и нуля или более цифр, которые могут быть разделены разделителями тысяч, за которыми необязательно следует десятичная точка и ноль или более цифр . Пустое число обрабатывается как '0'. Локаль 'LC_NUMERIC' определяет десятичный знак и разделитель тысяч. По умолчанию пробелом является пробел или табуляция, но языковой стандарт 'LC_CTYPE' может это изменить ".С POSIX Awk:
{ c = length m[c] = m[c] ? m[c] RS $0 : $0 } END { for (c in m) print m[c] }
пример
источник
1) чистое решение awk. Предположим, что длина строки не может быть больше> 1024, тогда
кошка имя файла | awk 'BEGIN {min = 1024; s = "";} {l = длина ($ 0); если (l <min) {min = l; s = $ 0;}} КОНЕЦ {print s} '
2) одно линейное решение bash, предполагающее, что все строки содержат только 1 слово, но может быть переработано для любого случая, когда все строки имеют одинаковое количество слов:
LINES = $ (имя файла кошки); для k в $ LINES; сделать printf "$ k"; эхо $ k | туалет -L; сделано | sort -k2 | голова -n 1 | вырезать -d "" -f1
источник
Вот многобайтовый метод сортировки строк по длине. Это требует:
wc -m
вам доступен (он есть в macOS).LC_ALL=UTF-8
. Вы можете установить это либо в вашем .bash_profile, либо просто добавив его перед следующей командой.testfile
имеет кодировку символов, соответствующую вашему языку (например, UTF-8).Вот полная команда:
cat testfile | awk '{l=$0; gsub(/\047/, "\047\"\047\"\047", l); cmd=sprintf("echo \047%s\047 | wc -m", l); cmd | getline c; close(cmd); sub(/ */, "", c); { print c, $0 }}' | sort -ns | cut -d" " -f2-
Объясняя по частям:
l=$0; gsub(/\047/, "\047\"\047\"\047", l);
← создает копию каждой строки в переменной awkl
и дважды экранирует каждую,'
чтобы строка могла безопасно отображаться как команда оболочки (\047
одинарная кавычка в восьмеричной системе счисления).cmd=sprintf("echo \047%s\047 | wc -m", l);
← это команда, которую мы выполним, которая отображает экранированную строкуwc -m
.cmd | getline c;
← выполняет команду и копирует возвращаемое значение счетчика символов в переменную awkc
.close(cmd);
← закрыть канал к команде оболочки, чтобы избежать ограничения системы на количество открытых файлов в одном процессе.sub(/ */, "", c);
← обрезает пробелы из значения количества символов, возвращаемогоwc
.{ print c, $0 }
← выводит количество символов в строке, пробел и исходную строку.| sort -ns
← сортирует строки (по количеству добавленных символов) численно (-n
) и поддерживает стабильный порядок сортировки (-s
).| cut -d" " -f2-
← удаляет добавленные значения счетчика символов.Он медленный (всего 160 строк в секунду на быстром Macbook Pro), потому что он должен выполнять подкоманду для каждой строки.
В качестве альтернативы, просто сделайте это только с помощью
gawk
(начиная с версии 3.1.5, gawk поддерживает многобайтовость), что будет значительно быстрее. Очень сложно выполнить все экранирование и двойные кавычки, чтобы безопасно передать строки через команду оболочки из awk, но это единственный метод, который я смог найти, который не требует установки дополнительного программного обеспечения (gawk по умолчанию недоступен в macOS).источник
используя Raku (ранее известный как Perl6)
~$ cat "BinaryAve.txt" | raku -e 'given lines() {.sort(*.chars).join("\n").say};' AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Atlantis,RI,12345,(999)123-5555,1.56 AS2345,ASDF1232, Mr. Plain Example, 110 Ternary ave.,Some City,RI,12345,(999)123-5555,1.56 AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Liberty City,RI,12345,(999)123-5555,1.56 AS2345,ASDF1232, Mrs. Plain Example, 1121110 Ternary st. 110 Binary ave..,Atlantis,RI,12345,(999)123-5555,1.56
Чтобы отменить сортировку, добавьте
.reverse
в середину цепочки вызовов методов - сразу после.sort()
. Вот код, который.chars
включает пробелы:~$ cat "number_triangle.txt" | raku -e 'given lines() {.map(*.chars).say};' (1 3 5 7 9 11 13 15 17 19 0) ~$ cat "number_triangle.txt" 1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 1 2 3 4 5 6 1 2 3 4 5 6 7 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 0
Вот сравнение времени между awk и Raku с использованием текстового файла размером 9,1 МБ от Genbank:
~$ time cat "rat_whole_genome.txt" | raku -e 'given lines() {.sort(*.chars).join("\n").say};' > /dev/null real 0m1.308s user 0m1.213s sys 0m0.173s ~$ #awk code from neillb ~$ time cat "rat_whole_genome.txt" | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2- > /dev/null real 0m1.189s user 0m1.170s sys 0m0.050s
HTH.
https://raku.org
источник