it-swarm.com.ru

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

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

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

foo
bar
baz

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

baz
bar
foo

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

532
Scotty Allen

Хвост BSD:

tail -r myfile.txt

Ссылка: FreeBSD , NetBSD , OpenBSD и OS X справочные страницы.

383
Jason Cohen

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

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

tac a.txt > b.txt
1217
Mihai Limbășan

Там есть известные трюки с седом :

# 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 :

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

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

Perl -e 'print reverse <>'

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

141
ephemient

Если вы оказались в vim используйте

:g/^/m0
53
DerMike
$ (tac 2> /dev/null || tail -r)

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

39
DigitalRoss
tac <file_name>

пример:

$ cat file1.txt
1
2
3
4
5

$ tac file1.txt
5
4
3
2
1
34
jins

Попробуйте следующую команду:

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

в конце вашей команды поставьте: | tac

tac делает именно то, что вы просите, он записывает каждый ФАЙЛ в стандартный вывод, в последнюю строку первым.

так это противоположность кошки :-).

17
Yakir GIladi Edry

Просто Баш :) (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
15
konsolebox

Самый простой способ - использовать команду tac. tac является обратным cat .. Пример.

$ cat order.txt
roger shah 
armin van buuren
fpga vhdl arduino c++ Java gridgain
$ tac order.txt > inverted_file.txt
$ cat inverted_file.txt
fpga vhdl arduino c++ Java gridgain
armin van buuren
roger shah 
11
Yekatandilburg

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

gawk '{ L[n++] = $0 } 
  END { while(n--) 
        print L[n] }' file
10
Tim Menzies

EDIT следующее генерирует случайным образом отсортированный список чисел от 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]))")
2
Yauhen Yakimovich

Для кросс-ОС (то есть OSX, Linux) решения, которые могут использовать tac внутри скрипта Shell, используйте homebrew, как уже упоминалось выше, затем просто псевдоним tac, например, так:

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

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

awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename
2
R. Kumar

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

tail -n20 file.txt | tac
2
ITNM

Если вы хотите изменить файл на месте, вы можете запустить

sed -i '1!G;h;$!d' filename

Это избавляет от необходимости создавать временный файл, а затем удалять или переименовывать оригинал и имеет тот же результат. Например:

$tac file > file2
$sed -i '1!G;h;$!d' file
$diff file file2
$

На основании ответа от ephemient , который сделал почти, но не совсем то, что я хотел.

1
Mark Booth

tail -r делает трюк:

сек 1 20 | хвост -r

0
Bohdan

Для пользователей Emacs: C-x h (выберите весь файл), а затем M-x reverse-region. Также работает только для выбора частей или линий и их возврата. 

0
Marius Hofert

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

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

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

0
KIC

Вы можете сделать это с vimstdin и 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 "[email protected]" +'%p | q!'

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

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

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

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

#!/usr/bin/env sh

vim -E "[email protected]" +'%p | q!' /dev/stdin
0
dosentmatter