Você pode fazer isso com vim
stdin
e stdout
. Você também pode usar ex
para ser compatível com POSIX . vim
é apenas o modo visual para ex
. De fato, você pode usar ex
com vim -e
ou vim -E
( ex
modo aprimorado ).
vim
é útil porque, ao contrário de ferramentas como sed
essa, armazena em buffer o arquivo para edição, enquanto sed
é usado para fluxos. Você pode usar awk
, mas terá que armazenar manualmente manualmente tudo em uma variável.
A ideia é fazer o seguinte:
- Leia de stdin
- Para cada linha, mova-o para a linha 1 (para reverter). Comando é
g/^/m0
. Isso significa globalmente, para cada linha g
; coincidir com o início da linha, que corresponde a qualquer coisa ^
; mova-o após o endereço 0, que é a linha 1m0
.
- Imprima tudo. Comando é
%p
. Isso significa para o intervalo de todas as linhas %
; imprima a linha p
.
- Saia vigorosamente sem salvar o arquivo. Comando é
q!
. Isso significa sair q
; com força !
.
# 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
Como tornar isso reutilizável
Eu uso um script que chamo ved
(como o editor vim sed
) para usar o vim para editar stdin
. Adicione isso a um arquivo chamado ved
no seu caminho:
#!/usr/bin/env sh
vim - --not-a-term -Es "$@" +'%p | q!'
Estou usando um +
comando em vez de +'%p' +'q!'
, porque o vim limita você a 10 comandos. Então, mesclá-los permite "$@"
que você tenha 9+
comandos em vez de 8.
Então você pode fazer:
seq 10 | ved +'g/^/m0'
Se você não possui o vim 8, insira isso ved
:
#!/usr/bin/env sh
vim -E "$@" +'%p | q!' /dev/stdin
perl -e 'print reverse <>'
mas provavelmente também se aplica a outros métodos).