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é.