Déplacer d'abord N lignes de sortie jusqu'à la fin sans utiliser de file temporaire

Imaginez une sortie d'une command comme

44444 55555 11111 22222 33333 

comment puis-je sortinger les N premières lignes (les deux premières dans l'exemple ci-dessus) et les append à la fin, mais sans utiliser de file temp (donc uniquement en utilisant des pipes)?

 11111 22222 33333 44444 55555 

Quelque chose comme le | sed -n '3,5p;1,2p' | sed -n '3,5p;1,2p' (qui évidemment ne fonctionne pas comme sed ne se soucie pas de l'ordre des commands).

Il suffit de copyr ces lignes dans le tampon de conservation (puis de les supprimer) et, à la dernière ligne, d'append le contenu de la memory tampon d'attente à l'espace de model:

 some command | sed '1,NUMBER{ # in this range H # append line to hold space and 1h # overwrite if it's the 1st line d # then delete the line } $G' # on last line append hold buffer content 

Avec gnu sed vous pouvez l'écrire comme

 some command | sed '1,NUMBER{H;1h;d;};$G' 

Voici un autre moyen avec ol ed (il affiche la sortie d' some command dans le tampon de text, puis m ove les lignes 1,NUMBER après le la $ t):

 ed -s <<IN r ! some command 1,NUMBERm$ ,p q IN 

Notez que – comme indiqué – ils échoueront tous les deux si la sortie a less de NUMBER +1 lignes. Une approche plus solide serait (syntaxe gnu sed ):

 some command | sed '1,NUMBER{H;1h;$!d;${g;q;};};$G' 

celle-ci ne supprime que les lignes de cette plage tant qu'elles ne sont pas la dernière ligne ( $!d ) – sinon elle écrase l'espace des patterns avec le contenu de la memory tampon ( g ) puis les q uits.

Une approche awk :

 cmd | awk -vn=3 ' NR <= n {head = head $0 "\n"; next} {print} END {printf "%s", head}' 

Un avantage sur l'approche sed @ don_crissti est qu'il fonctionne toujours (sort les lignes) si la sortie a n lignes ou less.

J'ai xclip et avec cela cela peut être fait de cette façon:

 ./a_command | xclip -in && xclip -o | tail -n +3 && xclip -o | head -n 2 

Voici sa description:

 xclip - command line interface to X selections (clipboard) NAME xclip - command line interface to X selections (clipboard) SYNOPSIS xclip [OPTION] [FILE]... DESCRIPTION Reads from standard in, or from one or more files, and makes the data available as an X selection for pasting into X applications. Prints current X selection to standard out. -i, -in read text into X selection from standard input or files (default) -o, -out prints the selection to standard out (generally for piping to a file or program) 

Une manière Perl:

 perl -ne '$.<3?($f.=$_):print;}{print $f' 

Ou, la même chose écrite less cryptiquement:

 perl -ne 'if($.<3){ $f.=$_ } else{ print } END{print $f}' 

Par exemple:

 $ cat file 44444 55555 11111 22222 33333 $ cat file | perl -ne '$.<3?($f.=$_):print;}{print $f' 11111 22222 33333 44444 55555 

Explication

  • -ne : lit le file d'input / stream ligne par ligne et applique le script donné par -e à chaque ligne.
  • $.<3 : $. est le numéro de ligne actuel, alors changez 3 en nombre de lignes que vous voulez décaler.
  • $.<3?($f.=$_):print; : c'est l'opérateur conditionnel, le format général est la condition ? case1 : case2 condition ? case1 : case2 , il exécutera case1 si la condition est true et case2 si elle est fausse. Ici, si le numéro de ligne actuel est inférieur à 3, il ajoute la ligne courante ( $_ ) à la variable $f et, si le numéro de ligne est supérieur à 3, il s'imprime.
  • }{ print $f : le }{ est un raccourci perl pour END{} . Il fonctionnera après que toutes les lignes d'input ont été traitées. À ce stade, nous aurons collecté toutes les lignes que nous voulons décaler et nous aurons imprimé tous ceux que nous voulons laisser seuls, alors imprimez les lignes sauvegardées comme $f .

Utilisez POSIX ex . Oui, il est destiné à l'édition de files, mais il fonctionnera dans un pipeline.

 printf %s\\n 111 222 333 444 555 | ex -sc '1,2m$|%p|q!' /dev/stdin 

Cela peut avoir des commands arbitraires ajoutées au début ou à la fin du pipeline et fonctionneront de la même façon. Mieux encore, étant donné la présence de /dev/stdin , il est conforme à POSIX.

(Je ne sais pas si /dev/stdin est spécifié dans POSIX ou pas, mais je vois qu'il est présent à la fois sous Linux et Mac OS X.)

Cela a un avantage de lisibilité par rapport à l'utilisation de l'espace de sed de sed – vous dites juste ex "déplacer ces lignes jusqu'à la fin" et il le fait. (Le rest des commands signifie "imprimer le tampon" et "sortir", qui sont également lisibles).

NB: La command ex donnée ci-dessus échouera si elle reçoit less de 2 lignes en input.

Pour en savoir plus:

  • Comment éditer des files non interactivement (par exemple dans un pipeline)?

Un petit extrait de python :

 #!/usr/bin/env python3 import sys file_ = sys.argv[1] lines = int(sys.argv[2]) with open(file_) as f: f = f.readlines() out = f[lines:] + f[:lines] print(''.join(out), end='') 

Passez le nom de file en tant que premier argument et le nombre de lignes à déplacer en tant que second argument.

Exemple:

 $ cat input.txt 44444 55555 11111 22222 33333 $ ./sw_lines.py input.txt 2 11111 22222 33333 44444 55555 $ ./sw_lines.py input.txt 4 33333 44444 55555 11111 22222 

Si vous pouvez stocker toute la sortie en memory:

 data=$(some command) n=42 # or whatever { tail -n +$((n+1)) <<<"$data"; head -n $n <<<"$data"; } > outputfile 

Voici une autre option qui nécessite GNU sed :

 (x=$(gsed -u 3q);cat;printf %s\\n "$x") 

-u rend GNU sed unbuffered afin que la command sed ne consum pas plus de 3 lignes de STDIN. La substitution de command supprime les lignes vides, de sorte que la command n'inclut pas de lignes vides à la fin de la sortie si les troisième, troisième et deuxième ou troisième, deuxième et première lignes sont vides.

Vous pouvez également faire quelque chose comme ceci:

 tee >(sponge /dev/stdout|sed -u 3q)|sed 1,3d 

Sans sponge /dev/stdout| la command échouerait avec de longues inputs. sponge /dev/stdout peut également être remplacé par tac|tac , même si cela imprime par exemple a\ncb si l'input est a\nb\nc\n est un saut de ligne, ou avec (x=$(cat);printf %s\\n "$x") , même si cela supprime les lignes vides de la fin de l'input. La command ci-dessus supprime la première ligne lorsque le nombre de lignes de l'input est un ou deux.