Сортировка по последнему полю строки
Каков самый простой способ сортировки списка строк, сортировка по последнему полю каждой строки? Каждая строка может иметь переменное количество полей.
Что-то вроде
sort -k -1
- это то, что я хочу, но sort (1) не принимает отрицательные числа для выбора полей с конца, а не для начала.
Я также хотел бы иметь возможность выбирать разделитель полей.
Изменить: Чтобы добавить определенность к вопросу: Список, который я хочу сортировать, - это список путей. Пути могут иметь произвольную глубину, следовательно, переменное число полей. Я хочу сортировать по компоненту имени файла.
Эта дополнительная информация может изменить то, как можно манипулировать линией для извлечения последнего поля (может использоваться базовое имя (1)), но не меняет требования к сортировке.
например.
/a/b/c/10-foo
/a/b/c/20-bar
/a/b/c/50-baz
/a/d/30-bob
/a/e/f/g/h/01-do-this-first
/a/e/f/g/h/99-local
Я хочу, чтобы этот список сортировался по именам файлов, которые начинаются с цифр, указывающих порядок чтения файлов.
Я добавил свой ответ, ниже которого я сейчас это делаю. Я надеялся, что есть более простой способ - возможно, другая утилита сортировки - возможно, без необходимости манипулировать данными.
Ответы
Ответ 1
Здесь находится командная строка Perl (обратите внимание, что ваша оболочка может потребовать от вас $
s):
perl -e "print sort {(split '/', $a)[-1] <=> (split '/', $b)[-1]} <>"
Просто соберите список в нем или, если список находится в файле, поместите имя файла в конец командной строки.
Обратите внимание, что этот script фактически не изменяет данные, поэтому вам не нужно быть осторожным в том, какой делиметр вы используете.
Здесь вывод образца:
>perl -e "print sort {(split '/', $a)[-1] <=> (split '/', $b)[-1]} " files.txt
/a/e/f/g/h/01-do-this-first
/a/b/c/10-foo
/a/b/c/20-bar
/a/d/30-bob
/a/b/c/50-baz
/a/e/f/g/h/99-local
Ответ 2
awk '{print $NF,$0}' file | sort | cut -f2- -d' '
В принципе, эта команда делает:
- Повторите последнее поле в начале, разделенное пробелом (по умолчанию OFS)
- Сортировка, разрешение дублированных имен файлов с использованием полного пути ($ 0) для сортировки
- Отрежьте повторяющееся первое поле, f2 - означает, что второе поле до последнего
Ответ 3
что-то вроде этого
awk '{print $NF"|"$0}' file | sort -t"|" -k1 | awk -F"|" '{print $NF }'
Ответ 4
Однострочный в perl для изменения порядка полей в строке:
perl -lne 'print join " ", reverse split / /'
Вы можете использовать его один раз, вывести вывод на сортировку, затем передать его обратно, и вы достигнете того, что хотите. Вы можете изменить / /
на / +/
, чтобы сжать пробелы. И вы, конечно, можете использовать любое регулярное выражение, которое хотите разбить линии.
Ответ 5
Я думаю, что единственным решением было бы использовать awk
:
- Поместите последнее поле вперед, используя
awk
.
- Сортировка строк.
- Поместите первое поле в конец снова.
Ответ 6
Замените последний разделитель на строке другим разделителем, который иначе не отображается в списке, сортируйте во втором поле, используя этот другой разделитель в качестве разделителя sort (1), а затем верните изменение разделителя.
delim=/
new_delim=" "
cat $list \
| sed "s|\(.*\)$delim|\1$new_delim|" \
| sort -t"$new_delim" -k 2,2 \
| sed "s|$new_delim|$delim|"
Проблема заключается в понимании того, какой разделитель использовать, который не отображается в списке. Вы можете сделать несколько проходов над списком, а затем grep для последовательности потенциальных разделителей, но все это довольно неприятно - особенно когда понятие "сортировка по последнему полю строки" так просто выражено, но решение не является.
Изменить: один безопасный разделитель для $new_delim - это NUL, поскольку он не может отображаться в именах файлов, но я не знаю, как поместить символ NUL в оболочку bourne/POSIX script (не bash) и будет ли sort и sed правильно обрабатывать его.
Ответ 7
#!/usr/bin/ruby
f = ARGF.read
lines = f.lines
broken = lines.map {|l| l.split(/:/) }
sorted = broken.sort {|a, b|
a[-1] <=> b[-1]
}
fixed = sorted.map {|s| s.join(":") }
puts fixed
Если все ответы связаны с perl или awk, может также решить все это на языке сценариев. (Кстати, я сначала попытался в Perl и быстро вспомнил, что мне не нравятся списки списков Perl. Мне бы хотелось увидеть версию Perl-гуру.)
Ответ 8
Я хочу, чтобы этот список отсортирован по именам файлов, которые начинаются с номеров указывая порядок чтения файлов.
find . | sed 's#.*/##' | sort
sed заменяет все части списка результатов, заканчивающихся косой чертой. имена файлов оставлены, и вы сортируете их.
Ответ 9
Вот версия Python oneliner, обратите внимание, что она предполагает, что поле является целым числом, вы можете изменить его при необходимости.
echo file.txt | python3 -c 'import sys; list(map(sys.stdout.write, sorted(sys.stdin, key=lambda x: int(x.rsplit(" ", 1)[-1]))))'
Ответ 10
| sed "s#(.*)/#\1"\\$'\x7F'\# \
| sort -t\\$'\x7F' -k2,2 \
| sed s\#\\$'\x7F'"#/#"
Еще хуже, чем простые отрицательные индексы полей для sort (1), но использование символа DEL в качестве разделителя не должно вызывать каких-либо проблем в этом случае.
Мне также нравится, насколько это симметрично.
Ответ 11
sort
позволяет указать разделитель с опцией -t
, если я хорошо его помню. Чтобы вычислить последнее поле, вы можете сделать что-то вроде подсчета числа разделителей в строке и суммировать их. Например, что-то вроде этого (предполагая разделитель ":" ):
d=`head -1 FILE | tr -cd : | wc -c`
d=`expr $d + 1`
($d
теперь содержит последний индекс поля).