Сортировка по последнему полю строки

Каков самый простой способ сортировки списка строк, сортировка по последнему полю каждой строки? Каждая строка может иметь переменное количество полей.

Что-то вроде

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 теперь содержит последний индекс поля).