Как я могу изменить порядок строк в файле? - PullRequest
558 голосов
/ 13 апреля 2009

Я хотел бы изменить порядок строк в текстовом файле (или стандартном вводе), сохранив содержимое каждой строки.

Итак, начиная с:

foo
bar
baz

Я бы хотел закончить с

baz
bar
foo

Существует ли для этого стандартная утилита командной строки UNIX?

Ответы [ 21 ]

0 голосов
/ 11 мая 2018

Вы можете сделать это с vim stdin и stdout. Вы также можете использовать ex, чтобы быть POSIX-совместимым . vim - это просто визуальный режим для ex. Фактически, вы можете использовать ex с vim -e или vim -E (улучшенный режим ex). vim полезен, потому что в отличие от таких инструментов, как sed, он буферизует файл для редактирования, а sed используется для потоков. Возможно, вы сможете использовать awk, но вам придется вручную буферизовать все в переменной.

Идея заключается в следующем:

  1. Читать со стандартного ввода
  2. Для каждой строки переместите ее в строку 1 (для реверса). Команда g/^/m0. Это означает глобально, для каждой строки g; соответствует началу строки, которая соответствует чему-либо ^; переместите его после адреса 0, который является строкой 1 m0.
  3. Распечатать все. Команда %p. Это означает для диапазона всех линий %; выведите строку p.
  4. Принудительно завершить работу без сохранения файла. Команда q!. Это значит выйти q; принудительно !.
# Generate a newline delimited sequence of 1 to 10
$ seq 10
1
2
3
4
5
6
7
8
9
10

# Use - to read from stdin.
# vim has a delay and annoying 'Vim: Reading from stdin...' output
# if you use - to read from stdin. Use --not-a-term to hide output.
# --not-a-term requires vim 8.0.1308 (Nov 2017)
# Use -E for improved ex mode. -e would work here too since I'm not
# using any improved ex mode features.
# each of the commands I explained above are specified with a + sign
# and are run sequentially.
$ seq 10 | vim - --not-a-term -Es +'g/^/m0' +'%p' +'q!'
10
9
8
7
6
5
4
3
2
1
# non improved ex mode works here too, -e.
$ seq 10 | vim - --not-a-term -es +'g/^/m0' +'%p' +'q!'

# If you don't have --not-a-term, use /dev/stdin
seq 10 | vim -E +'g/^/m0' +'%p' +'q!' /dev/stdin

# POSIX compliant (maybe)
# POSIX compliant ex doesn't allow using + sign to specify commands.
# It also might not allow running multiple commands sequentially.
# The docs say "Implementations may support more than a single -c"
# If yours does support multiple -c
$ seq 10 | ex -c "execute -c 'g/^/m0' -c '%p' -c 'q!' /dev/stdin

# If not, you can chain them with the bar, |. This is same as shell
# piping. It's more like shell semi-colon, ;.
# The g command consumes the |, so you can use execute to prevent that.
# Not sure if execute and | is POSIX compliant.
seq 10 | ex -c "execute 'g/^/m0' | %p | q!" /dev/stdin

Как сделать это многоразовым

Я использую скрипт, который я называю ved (редактор vim вроде sed), чтобы использовать vim для редактирования stdin. Добавьте это в файл с именем ved на вашем пути:

#!/usr/bin/env sh

vim - --not-a-term -Es "$@" +'%p | q!'

Я использую одну команду + вместо +'%p' +'q!', потому что vim ограничивает вас до 10 команд. Таким образом, объединение их позволяет "$@" иметь 9 + команд вместо 8.

Тогда вы можете сделать:

seq 10 | ved +'g/^/m0'

Если у вас нет vim 8, введите вместо этого ved:

#!/usr/bin/env sh

vim -E "$@" +'%p | q!' /dev/stdin
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...