J'aimerais inverser l'ordre des lignes dans un fichier texte (ou stdin), en préservant le contenu de chaque ligne.
Donc, c’est-à-dire en commençant par:
foo
bar
baz
Je voudrais finir avec
baz
bar
foo
Existe-t-il un utilitaire de ligne de commande UNIX standard pour cela?
Il y a les astuces sed bien connues :
# 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
(Explication: la ligne suivante doit être précédée d'une réserve pour conserver la mémoire tampon, la ligne d'échange et la mémoire tampon conservées, la ligne d'impression à la fin)
Alternativement (avec une exécution plus rapide) des awk one-liners :
awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file*
Si vous ne vous en souvenez pas,
Perl -e 'print reverse <>'
Sur un système avec les utilitaires GNU, les autres réponses sont plus simples, mais tout le monde n’est pas GNU/Linux ...
Si vous êtes en état d'utilisation vim
:g/^/m0
$ (tac 2> /dev/null || tail -r)
Essayez tac
, qui fonctionne sous Linux, et si cela ne fonctionne pas, utilisez tail -r
, qui fonctionne sous BSD et OSX.
tac <file_name>
exemple:
$ cat file1.txt
1
2
3
4
5
$ tac file1.txt
5
4
3
2
1
Essayez la commande suivante:
grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"
à la fin de votre commande, mettez: | tac
tac fait exactement ce que vous demandez, "écrivez chaque fichier sur la sortie standard, dernière ligne en premier."
tac est le contraire du chat :-).
Just 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
La méthode la plus simple utilise la commande tac
. tac
est cat
's inverse . Exemple:
$ 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
J'aime beaucoup la réponse " tail -r ", mais ma réponse gawk préférée est ....
gawk '{ L[n++] = $0 }
END { while(n--)
print L[n] }' file
EDIT Ce qui suit génère une liste de nombres triés au hasard de 1 à 10:
seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') **...**
où les points sont remplacés par la commande réelle qui inverse la liste
tac
seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(tac)
python: utiliser [:: - 1] sur 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]))")
Pour les solutions multi-OS (OSX, Linux) pouvant utiliser tac
dans un script Shell, utilisez homebrew comme d'autres l'ont mentionné ci-dessus, puis alias tac simplement comme ceci:
brew install coreutils
echo "alias tac='gtac'" >> ~/.bash_aliases (or wherever you load aliases)
source ~/.bash_aliases
tac myfile.txt
Cela fonctionnera à la fois sous BSD et GNU.
awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename
Meilleure solution:
tail -n20 file.txt | tac
Si vous souhaitez modifier le fichier à la place, vous pouvez exécuter
sed -i '1!G;h;$!d' filename
Cela supprime la nécessité de créer un fichier temporaire, puis de supprimer ou de renommer le fichier d'origine et a le même résultat. Par exemple:
$tac file > file2
$sed -i '1!G;h;$!d' file
$diff file file2
$
Basé sur le réponse par ephemient , qui a fait, mais pas tout à fait, ce que je voulais.
tail -r fait le tour:
seq 1 20 | queue -r
Je vois beaucoup d'idées intéressantes. Mais essayez mon idée. Ajoutez votre texte dans ceci:
rev | tr '\ n' '~' | rev | tr '~' '\ n'
qui suppose que le caractère '~' ne soit pas dans le fichier. Cela devrait fonctionner sur tous les shell UNIX remontant à 1961. Ou quelque chose comme ça.
Pour les utilisateurs Emacs: C-x h
(sélectionnez le fichier entier), puis M-x reverse-region
. Fonctionne également pour ne sélectionner que les pièces ou les lignes et inverser celles-ci.
J'avais la même question, mais je voulais aussi que la première ligne (en-tête) reste au premier plan. Donc j'avais besoin d'utiliser le pouvoir de 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 fonctionne également en cygwin ou gitbash
Vous pouvez le faire avec vim
stdin
et stdout
. Vous pouvez également utiliser ex
pour être conforme à POSIX . vim
est simplement le mode visuel pour ex
. En fait, vous pouvez utiliser ex
avec vim -e
ou vim -E
(mode ex
amélioré) .vim
est utile car contrairement aux outils comme sed
, il tamponne le fichier pour le modifier, alors que sed
est utilisé pour les flux. Vous pourrez peut-être utiliser awk
, mais vous devrez mettre manuellement tout le tampon dans une variable.
L'idée est de faire ce qui suit:
g/^/m0
. Cela signifie globalement, pour chaque ligne g
; correspond au début de la ligne, ce qui correspond à tout ^
; déplacez-le après l'adresse 0, qui correspond à la ligne 1 m0
.%p
. Cela signifie pour la plage de toutes les lignes %
; affiche la ligne p
.q!
. Cela signifie quit q
; avec force !
.# 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
Comment rendre ceci réutilisable
J'utilise un script que j'appelle ved
(éditeur de vim comme sed
) pour utiliser vim afin de modifier stdin
. Ajoutez ceci à un fichier appelé ved
dans votre chemin:
#!/usr/bin/env sh
vim - --not-a-term -Es "$@" +'%p | q!'
J'utilise une commande +
au lieu de +'%p' +'q!'
, car vim vous limite à 10 commandes. Donc, leur fusion permet au "$@"
d’avoir 9 commandes +
au lieu de 8.
Ensuite, vous pouvez faire:
seq 10 | ved +'g/^/m0'
Si vous n'avez pas vim 8, mettez ceci dans ved
à la place:
#!/usr/bin/env sh
vim -E "$@" +'%p | q!' /dev/stdin