Я пишу сценарий оболочки, используя любые общие команды UNIX. Я должен получить строку, которая имеет наименьшее количество символов (включая пробелы). Там может быть до около 20 строк.
Я знаю, что могу использовать, head -$L | tail -1 | wc -m
чтобы найти количество символов в строке L. Проблема в том, что единственный способ, который я могу придумать, используя это, состоит в том, чтобы вручную написать беспорядок операторов if, сравнивая значения.
Пример данных:
seven/7
4for
8 eight?
five!
Вернется, 4for
так как эта строка имеет наименьшее количество символов.
В моем случае, если несколько строк имеют самую короткую длину, должна быть возвращена одна. Неважно, какой из них выбран, если он имеет минимальную длину. Но я не вижу вреда в демонстрации обоих способов другим пользователям в других ситуациях.
источник
Ответы:
Perl способ. Обратите внимание, что если имеется много строк одинаковой, самой короткой длины, этот подход выведет только одну из них:
объяснение
perl -lne
:-n
означает «читать входной файл построчно»,-l
приводит к удалению завершающих строк новой строки из каждой строки ввода и новой строки для каждогоprint
вызова; и-e
это сценарий, который будет применяться к каждой строке.$m//=$_
: установить$m
текущую строку ($_
), если$m
не определено.//=
Оператор доступен , так как Perl 5.10.0.$m=$_ if length()<length($m)
: если длина текущего значения$m
больше, чем длина текущей строки, сохраните текущую строку ($_
) как$m
.END{print $m if $.}
: после обработки всех строк выведите текущее значение$m
самой короткой строки. Этоif $.
гарантирует, что это происходит только тогда, когда определен номер строки ($.
), что позволяет избежать печати пустой строки для пустого ввода.Кроме того, поскольку ваш файл достаточно мал, чтобы поместиться в памяти, вы можете сделать:
объяснение
@K=sort{length($a) <=> length($b)}<>
:<>
здесь массив, элементы которого являются строками файла. Ониsort
будут отсортированы по длине, а отсортированные строки сохранены в виде массива@K
.print "$K[0]"
: вывести первый элемент массива@K
: самую короткую строку.Если вы хотите распечатать все самые короткие строки, вы можете использовать
источник
-C
чтобы измерить длину с точки зрения количества символов вместо количества байтов. В локали UTF-8$$
имеет меньше байтов, чем€
(2 против 3), но больше символов (2 против 1).С
sqlite3
:источник
strace
указывает). Если вам нужно работать с действительно большими файлами (а ваша система не меняет местами), вы можете принудительно установить его, просто добавив имя файла, например,sqlite3 $(mktemp)
и все данные будут записаны на диск.Вот вариант
awk
решения для печати первой найденной минимальной строки:который может быть просто расширен на одно условие для печати всех минимальных строк:
источник
Python выходит довольно лаконичным, а код делает то, что говорит на олове:
python -c "import sys; print min(sys.stdin, key=len),"
Признаюсь, последняя запятая неясна. Это препятствует тому, чтобы оператор печати добавил дополнительный перенос строки. Кроме того, вы можете написать это в Python 3, поддерживая 0 строк, например:
python3 -c "import sys; print(min(sys.stdin, key=len, default='').strip('\n'))"
источник
Я всегда люблю решения с использованием чистых сценариев оболочки (без exec!).
Примечание :
Существует проблема с байтами NUL на входе. Итак,
printf "ab\0\0\ncd\n" | bash this_script
печатаетab
вместоcd
.источник
bash
убедила бы меняsort
вместо этого направить промежуточный результат .var=$(get data)
потому что он ограничивает поток данных одним контекстом - но когда вы перемещаете данные через конвейер - в потоке - каждый прикладной exec обычно полезен - потому что он позволяет применение модульных программ только при необходимости.$IFS
не различает цифры - даже если в$IFS
значении по умолчанию их нет , хотя многие оболочки будут принимать предустановленную конфигурацию среды$IFS
- и поэтому это не особенно надежное значение по умолчанию./bin/sh
. Это случалось со мной несколько раз с хостами SunOS4 с/usr
потерянными или некоторыми.so
повреждениями, и теперь, в современном веке Linux, я все еще время от времени сталкиваюсь с подобными ситуациями со встроенными системами или initrd систем с ошибками загрузки. BusyBox - одна из замечательных вещей, которые мы недавно приобрели.Здесь чистое
zsh
решение (оно печатает все строки с минимальной длиной, изfile
):Пример ввода:
Выход:
Я думаю, что нужно краткое объяснение :-)
Сначала мы устанавливаем внутренний разделитель полей на новую строку:
Пока все хорошо, теперь самая сложная часть.
print
использует-l
флаг для печати результата, разделенного символами новой строки вместо пробелов.Теперь мы начнем с внутренней стороны:
Файл читается построчно и рассматривается как массив. Затем:
o
Флаг говорит о том , что результат должен быть заказан в порядке возрастания, на@
средства для лечения результата в виде массива тоже. Часть, стоящая за (//?/?
), является заменой и заменяет все символы на?
. В настоящее время:Мы берем первый элемент массива
[1]
, который является самым коротким, в вашем случае его сейчас????
.Сопоставление выполняется для каждого элемента массива отдельно, а несопоставленные элементы массива удаляются (
M
). Каждый элемент, который соответствует????
(4 символа) остается в массиве. Таким образом, остальные элементы имеют 4 символа (самые короткие).Изменить: Если вам нужна только одна из самых коротких строк, эта измененная версия печатает первую:
источник
... и победитель ... строка 2, казалось бы.
Но проблема в том, что каждая строка должна быть более чем в два раза длиннее, чтобы она работала - поэтому LINE_MAX эффективно уменьшается вдвое. Причина в том, что он использует - что, база 1? - представлять длину линии. Подобный и, возможно, более аккуратный подход может заключаться в сжатии этой информации в потоке. Первая идея, которая приходит мне в голову, заключается в том, что я должен
unexpand
:Это печатает ...
Еще один, просто
sed
:Синтаксис соответствует стандартам - но это не гарантирует, что любой старый
sed
справится\(reference-group\)\{counts\}
правильно - многие этого не делают.Он в основном применяет одно и то же регулярное выражение для ввода многократно - что может быть очень полезно, когда пришло время их компилировать. Этот шаблон:
Который по-разному соответствует различным строкам. Например:
... совпадает с
s
in\1
и''
нулевой строкой in\2
.... сочетается с
1
в\1
и\nstring2\nstring3
в\2
... совпадает с
\n
in\1
и''
нулевой строкой in\2
. Это было бы проблематично, если бы была какая-либо вероятность появления\n
ewline в начале пространства шаблонов, но для предотвращения этого используются команды/^\n/D
, и//!g
. Я использовал,[^\n]
но другие потребности в этом небольшом скрипте сделали переносимость проблемой, и я не был удовлетворен многими путями, которые он часто неверно истолковывает. Плюс,.
быстрее.... матч
\n
иs
снова в,\1
и оба получают''
нулевую строку\2
. Пустые строки не совпадают вообще.Когда шаблон применяется
g
лобально, два смещения - как крайнее левое стандартное смещение, так и меньшее правое смещение на боковой\n
линии - уравновешиваются, чтобы вызвать пропуск. Несколько примеров:... если все применяются (не в последовательности) к следующей строке ...
... превратит его в ...
В основном я использую регулярное выражение, чтобы всегда обрабатывать только первую строку в любом шаблонном пространстве, к которому я его применяю. Это позволяет мне манипулировать двумя различными версиями как сохраненной строки с кратчайшим совпадением, так и самой последней строки, не прибегая к циклам тестирования - каждая примененная замена обрабатывает все пространство шаблона одновременно.
Различные версии необходимы для сравнения строк и строк - поэтому должна быть версия каждой строки, в которой все символы гарантированно равны. Но, конечно, если одна или другая из них на самом деле окажутся самой ранней из самых коротких строк на входе, то строка, напечатанная для вывода, вероятно, должна быть исходной версией строки, а не той, которую я санировал / гомогенизировал для сравнения. И поэтому мне нужны две версии каждого.
К сожалению, еще одной необходимостью является многократное переключение буфера для обработки одного и того же, но, по крайней мере, ни один из буферов никогда не превышает больше, чем четыре строки, необходимые для поддержания актуальности, - и поэтому, возможно, это не страшно.
Во всяком случае, для каждого цикла первое, что происходит, - это преобразование запомненной строки, потому что единственная фактически сохраненная копия - это буквальный оригинал - в ...
... и после этого
n
строка ввода ext перезаписывает любой старый буфер. Если он не содержит хотя бы одного символа, он фактически игнорируется. Было бы намного проще простоq
использовать первую появившуюся пустую строку, но в моих тестовых данных их было много, и я хотел обработать несколько абзацев.И поэтому, если он содержит символ, его буквальная версия добавляется к запомненной строке, а его версия с разнесенным сравнением располагается в начале пространства шаблонов, например так:
Последнее замещение применяется к этому образцу пространства:
Таким образом, если символ новой строки может уместиться в пределах пространства, необходимого для хранения запомненной строки, по крайней мере, с одним запасным символом, тогда первые две строки заменяются, в противном случае - только первая.
Независимо от результата, первая строка в шаблонном пространстве всегда
D
выбирается в конце цикла, прежде чем начинать снова. Это означает, что если новая строка короче последней строки ...... отправляется обратно к первой замене в цикле, которая всегда будет удаляться только с первого символа новой строки - и поэтому она остается целой. Но если это не так, строка ...
... вместо этого начнется следующий цикл, и первая замена удалит из него строку ...
...каждый раз.
В самой последней строке запомненная строка выводится на стандартный вывод, поэтому для приведенных данных примера она печатает:
Но, если серьезно, используйте
tr
.источник
REINPUT | sort -t: -nk1,1 | cut -d: -f3-
. И второе - это просто включить еще одинsed
--expression
скрипт в хвост.sort
поведение «s в качестве связующего выключателя , когда же длина линия возникает на входе - так самые ранние встречающиеся линии всегда всплывает на поверхность в этом случае.Пытаться:
Идея состоит в том,
awk
чтобы сначала напечатать длину каждой строки. Это будет выглядеть как:Затем используйте счетчик символов для сортировки строк
sort
,cut
чтобы избавиться от счетчика иhead
сохранить первую строку (ту, которая содержит наименьшее количество символов). Вы можете, конечно, использовать,tail
чтобы получить строку с наибольшим количеством символов в этом случае.(Это было принято из этого ответа )
источник
head -1
tail
(так какhead
можно завершить работу, как только ее работа будет выполнена, без чтения оставшейся части ее ввода).С POSIX awk:
источник
L
было лучшее письмо, чтобы выбрать имя переменной: D Нечто подобноеmin
прояснитЗаимствование некоторых идей @ mikeserv:
Первый
sed
делает следующее:h
сохраняет исходную строку в буфер хранения:
- чтобы исключить опасность внедрения кодаexpr length "whole line"
- это выражение оболочки, которое может быть оцененоs
является СЭД расширение GNU для оценки пространства шаблонов и возврата результата в пространство шаблонов.G
добавляет новую строку и содержимое области удержания (исходной строки) в пространство шаблонаs
заменяет перевод строки на вкладкуКоличество символов теперь является числом в начале каждой строки, поэтому
sort -n
сортируется по длине строки.Затем финал
sed
удаляет все, кроме первой (самой короткой) строки и длины строки, и печатает результат.источник
expr
здесь лучше. Да,e
появится оболочка для каждой строки. Я отредактировал выражение sed таким образом, чтобы оно заменяло каждый символ в строке символом:
перед символом eval, что, по-моему, должно исключить любую возможность внедрения кода.xargs expr
лично - но, кроме того, чтобы избегать промежуточной оболочки, это, вероятно, более стилистическая вещь. Во всяком случае, мне это нравится.Мне пришло в голову, что все это возможно в одном
sed
выражении. Это не красиво:Разбивая это:
BSD sed в OS X немного более привередлив с символами новой строки. Эта версия работает как для BSD, так и для GNU версий sed:
Обратите внимание, что это скорее ответ «потому что это возможно», чем серьезная попытка дать ответ передовой практики. Я думаю, это значит, что я слишком много играю
источник
man sed
На OS X: «escape-последовательность \ n соответствует символу новой строки, встроенному в пространство образца» . Поэтому я думаю, что GNU sed допускает\n
регулярное выражение и замену, тогда как BSD допускает только\n
регулярное выражение, а не замену.\n
из пространства шаблонов является хорошей идеей и будет работать во второмs///
выражении, ноs/.*/&\n&/
выражение вставляет\n
в пространство шаблонов, где его раньше не было. Также для BSD sed, по-видимому, требуются буквальные символы новой строки после определений меток и ветвей.sed
скрипт должен быть текстовым файлом, за исключением того, что он не должен заканчиваться переводом строки . Таким образом, вы обычно можете разделять их как отдельные аргументы -sed -e :\ label -e :\ label2
и так далее. Так как вы все1h
равно делаете , вы можете просто переключиться на некоторую логику, основанную наx;H
получении новой строки - и вы можете урезать ведущую новую строку из пространства образца в конце цикла, не вытягивая новую строку с /D
.G
первое и изменивs///
выражение. Разделение на части с помощью-e
позволяет всем идти по одной (длинной) строке без буквальных переносов строк.\n
Эвакуации билд дляsed
«s LHS, также, и я думаю , что это утверждение спецификации дословно, за исключением того, что выражения скобки POSIX также билд таким образом , чтобы все символы теряют свое особое значение - (явно включая\\
) - в пределах одного, за исключением скобок, тире как разделитель диапазона и точка, равно, каретка, двоеточие для сопоставления, эквивалентности, отрицания и классов.Другое решение Perl: хранить строки в хеш-массивах, ключом хеша является длина строки. Затем распечатайте строки с ключом минимума.
источник
push @{$lines{+length}};
иprint @{$lines{+min keys %lines}};
для меньшего набора текста :)perl -MList::Util=min -nE'push @{$l{+length}},$_}END{say@{$l{min keys%l}}' sample
perl
становится немного грубовато для тех из нас, кто не в порядкеperl
с загадочной природой. КСТАТИ. игра в гольфsay
выводит ложную пустую строку в конце вывода.Чтобы получить только первую короткую строку:
Чтобы получить все кратчайшие строки, просто измените
{p;q}
наp
Другой метод (несколько необычный) состоит в том, чтобы
sort
выполнять фактическую сортировку по длине . Это относительно медленно даже с короткими линиями, и становится значительно медленнее, когда длина линии увеличивается.Тем не менее, я нахожу идею сортировки по перекрывающимся ключам довольно интересной. Я публикую это на тот случай, если другие могут посчитать это интересным / информативным.
Как это работает:
Сортировка по длинам-вариантам одного и того же ключа -
key 1
который охватывает всю строку.Каждый последующий вариант ключа увеличивает длину ключа на один символ до длины самой длинной строки файла (определяется по
wc -L
)Чтобы получить только первую (отсортированную) самую короткую строку:
что так же, как:
источник
Предполагая, что пустые строки не считаются самыми короткими, и что пустые строки могут существовать, будет работать следующий чистый AWK:
источник
Как насчет использования сортировки?
источник
С GNU awk
Считайте каждую строку в массив, индексированный по длине строки.
Установите
PROCINFO["sorted_in"]
для@ind_num_asc
принудительного сканирования массива упорядочения массива по индексу массива, отсортированному по номерамУстановка
PROCINFO
описанным выше способом заставляет линию с наименьшей длиной быть выбранной первой при обходе массива. Так что выведите первый элемент из массива и выйдитеНедостатком является
nlogn
то, что некоторые другие подходыn
вовремяисточник
Среднеуровневый инструмент оболочки, без
sed
илиawk
:источник
$f
переменной; У меня есть мнение , что может быть возможно с использованиемtee
как - то ...