Подтвердить что ты не робот

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

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

Что-то вроде

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

Я хочу, чтобы этот список сортировался по именам файлов, которые начинаются с цифр, указывающих порядок чтения файлов.

Я добавил свой ответ, ниже которого я сейчас это делаю. Я надеялся, что есть более простой способ - возможно, другая утилита сортировки - возможно, без необходимости манипулировать данными.

4b9b3361

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