Как изменить порядок строк в файле?
Я хотел бы изменить порядок строк в текстовом файле (или stdin), сохранив содержимое каждой строки.
Итак, т. е., начиная с:
foo
bar
baz
Я хотел бы закончить с
baz
bar
foo
существует ли стандартная утилита командной строки UNIX для этого?
20 ответов
здесь известные трюки 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...
$ (tac 2> /dev/null || tail -r)
попробовать tac
, который работает в Linux, и если это не работает, используйте tail -r
, который работает на BSD и OSX.
Просто 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
редактировать ниже генерируется случайно отсортированный список чисел от 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
, но вам придется вручную буферизировать все в переменная.
идея в том, чтобы сделать следующее:
- читать из stdin
- для каждой строки переместите ее в строку 1 (для обратного хода). Команда
g/^/m0
. Это означает глобально, для каждой строкиg
; соответствует началу строки, которая соответствует чему-либо^
; переместите его после адреса 0, который является строкой 1m0
. - печатать все. Команда
%p
. Это означает для диапазона всех линий%
; печать строкиp
. - принудительно выйти без сохранения файла. Команда
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
с помощью Visual Studio™ можно выбрать нижнюю строку с помощью мыши. "вырезать" эту строку, а затем "вставить" ее сверху. Продолжайте делать это до тех пор, пока исходная верхняя линия не окажется внизу. Я считаю, что этот метод работает в других редакторах, но я не пробовала.