Как изменить порядок строк в файле?

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

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

foo
bar
baz

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

baz
bar
foo

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

20 ответов


хвост BSD:

tail -r myfile.txt

ссылки: FreeBSD, NetBSD, OpenBSD и OS X руководство страниц.


также стоит упомянуть: tac (ГМ, обратный cat). Часть coreutils.

листать один файл в другой

tac a.txt > b.txt

здесь известные трюки sed:

# reverse order of lines (emulates "tac")
# bug/feature in HHsed v1.5 causes blank lines to be deleted
sed '1!G;h;$!d'               # method 1
sed -n '1!G;h;$p'             # method 2

(объяснение: добавить не начальную строку для хранения буфера, линии подкачки и буфера, распечатать строку в конце)

альтернативно (с более быстрым выполнением) от awk One-liners:

awk '{a[i++]=} END {for (j=i-1; j>=0;) print a[j--] }' file*

Если вы не можете вспомнить, что,

perl -e 'print reverse <>'

в системе с утилитами GNU другие ответы проще, но не весь мир GNU/Linux...


Если вам случится быть в vim использовать

:g/^/m0

$ (tac 2> /dev/null || tail -r)

попробовать tac, который работает в Linux, и если это не работает, используйте tail -r, который работает на BSD и OSX.


tac <file_name>

пример:

$ cat file1.txt
1
2
3
4
5

$ tac file1.txt
5
4
3
2
1

попробуйте выполнить следующую команду:

grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print  }"

Просто Bash :) (4.0+)

function print_reversed {
    local lines i
    readarray -t lines

    for (( i = ${#lines[@]}; i--; )); do
        printf '%s\n' "${lines[i]}"
    done
}

print_reversed < file

самый простой метод использует


Мне очень нравится " хвост-r" ответ, но мой любимый ответ gawk....

gawk '{ L[n++] =  } 
  END { while(n--) 
        print L[n] }' file

в конце вашей команды put: | tac


лучшее решение:

tail -n20 file.txt | tac

редактировать ниже генерируется случайно отсортированный список чисел от 1 до 10:

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') **...**

где точки заменяются фактической командой, которая меняет список

tac

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(tac)

python: использование [:: -1] в sys.как stdin

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(python -c "import sys; print(''.join(([line for line in sys.stdin])[::-1]))")

для решения cross OS (т. е. OSX, Linux), которое может использовать tac внутри сценария оболочки используйте homebrew, как упоминалось выше, а затем просто псевдоним tac так:

brew install coreutils
echo "alias tac='gtac'" >> ~/.bash_aliases (or wherever you load aliases)
source ~/.bash_aliases
tac myfile.txt

Это будет работать как на BSD, так и на GNU.

awk '{arr[i++]=} END {while (i>0) print arr[--i] }' filename

У меня был тот же вопрос, но я также хотел, чтобы первая строка (заголовок), чтобы остаться на вершине. Поэтому мне нужно было использовать силу awk

cat dax-weekly.csv | awk '1 { last = NR; line[last] = ; } END { print line[1]; for (i = last; i > 1; i--) { print line[i]; } }'

PS также работает в cygwin или gitbash


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

идея в том, чтобы сделать следующее:

  1. читать из stdin
  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'

если у вас нет ВИМ 8 и ved вместо:

#!/usr/bin/env sh

vim -E "$@" +'%p | q!' /dev/stdin

хвост-r не работает:

seq 1 20 / tail-r


с помощью Visual Studio™ можно выбрать нижнюю строку с помощью мыши. "вырезать" эту строку, а затем "вставить" ее сверху. Продолжайте делать это до тех пор, пока исходная верхняя линия не окажется внизу. Я считаю, что этот метод работает в других редакторах, но я не пробовала.


sort -r < filename

или

rev < filename