По умолчанию sort
сортирует символ за символом, используя порядок сортировки, заданный локалью. Как правило, это довольно близко к порядку ASCII, но могут быть некоторые региональные различия. Со страницы руководства:
*** WARNING *** The locale specified by the environment affects sort order.
Set LC_ALL=C to get the traditional sort order that uses native byte values.
Собственное значение байта обычно означает значение ASCII, поэтому цифры идут перед прописными буквами, которые идут перед строчными буквами. Но порядок по-прежнему посимвольный, поэтому 10
предшествует 2
, потому что 1
предшествует 2
.
Когда указана опция -n
или --numeric-sort
, ряды цифр обрабатываются как числа (, а не отдельные символы ), и сортируются в числовом порядке от наименьшего числа к наибольшему.
Документация не полностью описывает детали, поэтому здесь приведены правила флага -n
, полученные экспериментально:
Соблюдать:
$ printf %s\\n 2z 111 10 20b 20a aa2 aa10 | sort -n
aa10
aa2
2z
10
20a
20b
111
По правилу 3 строки aa10
и aa2
обрабатываются как нули и сортируются по оставшимся символам (, включая цифры, которые считаются символами ).
По правилу 2 строки 2z
, 20a
и 20b
обрабатываются как числа, и завершающий символ вступает в силу только тогда, когда числа совпадают.
А по правилу 1 все строки, начинающиеся с цифры, сортируются по числовому значению.
Без флага -n
сортировка выполняется посимвольно, где цифровые символы предшествуют буквенным. Соблюдайте:
$ printf %s\\n 2z 111 10 20b 20a aa2 aa10 | sort
10
111
20a
20b
2z
aa10
aa2
Мы действительно можем обойтись без cut
, если захотим:
while IFS=, read _ _ _ stamp _ ; do
echo "do something with $stamp here"
done < "$file"
Немного разобрать:
IFS=,
временно установить разделитель записей на,
read _ _ _ stamp _
сохранить поля 1 -3 и 5 и далее в одноразовой переменной, а поле 4 (записать дату/время )какstamp
< "$file"
читаем в исходном файле (это ловится нашей командой read
).
Другой способ скрыть эту кошку, заполнив массив каждой строкой (это точно работает в bash, другие оболочки могут не поддерживать это или реализовать по-другому):
while IFS=, read -a line ; do echo "${line[3]}" ; done < "$file"
Когда вы перебираете подстановку команд без кавычек $(cat $file)
, вы перебираете все слова, которые приводят к результату cat $file
. Словом будет любое слово, ограниченное пробелом, (пробелом, табуляцией или новой строкой. по умолчанию ). Это означает, что для строки в $file
, то есть
883427446627317909,1114259,1573178423,2019-11-08 02:00:23,RD,4.7,0,351442429
у вас будет два слова 883427446627317909,1114259,1573178423,2019-11-08
и 02:00:23,RD,4.7,0,351442429
(, то есть цикл будет выполняться дважды для этой единственной строки ). Это означает, что вы получите 2019-11-08
в первой итерации и 0
во второй из этой строки.
Решение состоит в том, не заключать подстановку команды в кавычки, так как это приведет к повторению цикла один раз по всему содержимому файла, которое считывается в $line
. Решение не в установке IFS
на новую строку, поскольку это неэлегантно(потребовало бы одного вызова cut
на каждой итерации ).
Вместо этого разберите нужные данные одним вызовом cut
и прочитайте это:
while IFS= read -r datetime; do
# use "$datetime" here
done < <( cut -d, -f4 "$file" )
При этом используется подстановка процесса для создания входного потока для цикла while
для чтения. Данные в этом потоке будут состоять из четвертого поля, разделенного запятой -, в файле с именем $file
.
В качестве альтернативы, с циклом while
в подоболочке:
cut -d, -f4 "$file" |
while IFS= read -r datetime; do
# use "$datetime" here
done
С awk
,обработка будет чище (, если вам не нужно использовать значение даты/времени в качестве переменной оболочки по какой-либо причине):
awk -F, '{ datetime = $4;... more code here using the datetime variable }' "$file"
Похожие материалы: