Comment puis-je inverser l'ordre des lignes dans un fichier?

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?

509
demandé sur Good Person 2009-04-13 01:34:53

20 réponses

Queue BSD:

tail -r myfile.txt

Référence: FreeBSD, NetBSD, OpenBSD et OS X, les pages de manuel.

356
répondu Jason Cohen 2014-12-09 17:50:02

Il convient également de mentionner: tac (Le, ahem, inverse de cat). Une partie de coreutils.

Retourner un fichier dans un autre

tac a.txt > b.txt
1181
répondu Mihai Limbășan 2016-05-18 16:28:33

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: ajouter la ligne non initiale pour maintenir le tampon, permuter la ligne et maintenir le tampon, Imprimer la ligne à la fin)

Alternativement (avec une exécution plus rapide) à partir des one-liners awk :

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 des utilitaires GNU, les autres réponses sont plus simples, mais tout le monde N'est pas GNU / Linux...

133
répondu ephemient 2016-07-08 19:17:37

Si vous êtes dans vim Utilisez

:g/^/m0
52
répondu DerMike 2011-11-29 14:27:16
$ (tac 2> /dev/null || tail -r)

Essayez tac, qui fonctionne sur Linux, et si cela ne fonctionne pas, utilisez tail -r, qui fonctionne sur BSD et OSX.

38
répondu DigitalRoss 2012-12-13 20:25:01
tac <file_name>

Exemple:

$ cat file1.txt
1
2
3
4
5

$ tac file1.txt
5
4
3
2
1
34
répondu jins 2017-07-20 08:11:09

Essayez la commande suivante:

grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"
22
répondu kenorb 2015-02-23 12:54:07

Juste 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
15
répondu konsolebox 2018-07-25 14:16:43

La méthode la plus simple consiste à utiliser la commande tac. tac est l'inverse de cat. 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 
12
répondu Yekatandilburg 2015-02-14 20:16:20

J'aime vraiment la réponse" tail-R ", mais ma réponse préférée est gawk....

gawk '{ L[n++] = $0 } 
  END { while(n--) 
        print L[n] }' file
10
répondu Tim Menzies 2012-10-08 03:21:31

À la fin de votre commande mettre: | tac

6
répondu Yakir Edry GIladi 2018-08-08 10:24:20

Meilleure solution:

tail -n20 file.txt | tac
3
répondu ITNM 2016-12-01 01:05:16

Modifier ce qui suit génère une liste aléatoire de nombres de 1 à 10:

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

Où les points sont remplacés par une 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]))")
2
répondu Yauhen Yakimovich 2013-04-02 18:05:48

Pour la solution cross OS (c'est-à-dire OSX, Linux) qui peut utiliser tac dans un script shell, utilisez homebrew comme d'autres l'ont mentionné ci-dessus, puis alias tac comme ceci:

brew install coreutils
echo "alias tac='gtac'" >> ~/.bash_aliases (or wherever you load aliases)
source ~/.bash_aliases
tac myfile.txt
2
répondu lacostenycoder 2017-03-28 16:51:46

Cela fonctionnera à la fois sur BSD et GNU.

awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename
2
répondu R. Kumar 2018-04-05 15:26:48

J'ai eu la même question, mais je voulais aussi que la première ligne (en-tête) reste au top. J'avais donc besoin d'utiliser la puissance 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 dans cygwin ou gitbash

0
répondu KIC 2016-04-19 08:53:43

, Vous pouvez le faire avec vim stdin et stdout. Vous pouvez également utiliser ex pour être conforme POSIX . vim est juste 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 à des outils comme sed, il met en mémoire tampon le fichier pour l'édition, tandis que sed est utilisé pour les flux. Vous pourriez être en mesure d'utiliser awk, mais vous devrez tout mettre en mémoire tampon manuellement dans une variable.

L'idée est de faire le suivant:

  1. lire à partir de stdin
  2. pour chaque ligne, déplacez-la sur la ligne 1 (pour inverser). La commande est g/^/m0. Cela signifie globalement, pour chaque ligne g; correspond au début de la ligne, qui correspond à n'importe quoi ^; déplacez-le après l'adresse 0, qui est la ligne 1 m0.
  3. Imprimez tout. La commande est %p. Cela signifie pour la plage de toutes les lignes %; Imprimer la ligne p.
  4. quitter avec force sans enregistrer le fichier. La commande est q!. Cela signifie quitter 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 le rendre réutilisable

J'utilise un script que j'appel de ved (éditeur vim comme sed) à utiliser vim pour éditer 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. Ainsi, les fusionner permet aux "$@" d'avoir 9 commandes + au lieu de 8.

, Alors vous pouvez faire:

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

Si vous n'avez pas vim 8, mettez ceci ved au lieu de cela:

#!/usr/bin/env sh

vim -E "$@" +'%p | q!' /dev/stdin
0
répondu dosentmatter 2018-05-11 19:46:58

Queue -r est l'astuce:

Seq 1 20 / tail-r

0
répondu Bohdan 2018-06-15 22:28:14

En utilisant Visual Studio™, vous pouvez sélectionner la ligne de fond avec la souris. "couper" cette ligne, puis 'coller' le top. Continuez à le faire jusqu'à ce que la ligne supérieure d'origine soit en bas. Je crois que cette méthode fonctionne dans d'autres éditeurs mais je ne l'ai pas essayé.

-1
répondu Dr Blowhard 2018-06-13 08:26:22
sort -r < filename

Ou

rev < filename
-5
répondu Yoker Rekoy 2016-04-21 22:03:26