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?
20 réponses
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
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...
$ (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.
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 }"
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
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
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
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]))")
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
Cela fonctionnera à la fois sur BSD et GNU.
awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename
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
, 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:
- lire à partir de stdin
- pour chaque ligne, déplacez-la sur la ligne 1 (pour inverser). La commande est
g/^/m0
. Cela signifie globalement, pour chaque ligneg
; correspond au début de la ligne, qui correspond à n'importe quoi^
; déplacez-le après l'adresse 0, qui est la ligne 1m0
. - Imprimez tout. La commande est
%p
. Cela signifie pour la plage de toutes les lignes%
; Imprimer la lignep
. - quitter avec force sans enregistrer le fichier. La commande est
q!
. Cela signifie quitterq
; 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
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é.