一時ファイルを使用せずに、出力の最初のN行を最後まで移動します


11

次のようなコマンドの出力を想像してください

44444
55555
11111
22222
33333

最初のN行(上記の例では最初の2行)をヤンクして、最後にそれらを追加できますが、一時ファイルを使用せずに(パイプのみを使用して)どうすればよいですか?

11111
22222
33333
44444
55555

線に沿って何かが| sed -n '3,5p;1,2p'(コマンドの順序を気にしませんsedのようこれは明らかに動作しません)。


2
一時ファイルを使用できないのはなぜですか?
Braiam

回答:


13

これらの行をホールドバッファーにコピーし(その後、削除し)、最後の行でホールドバッファーの内容をパターンスペースに追加します。

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

ではgnu sed、あなたはとしてそれを書くことができます

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

次に、ol 'を使用した別の方法を示しますedr出力をsome commandテキストバッファーにm送り、その後、最初の行の1,NUMBER後に行を移動します$)。

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

指摘したように、出力がNUMBER+1行未満の場合、これらは両方とも失敗することに注意してください。より堅固なアプローチは(gnu sed構文)です:

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

これは、最後の行($!d)でない限り、その範囲の行のみを削除します。それ以外の場合は、パターンスペースをホールドバッファーの内容(g)で上書きし、次にquit(現在のパターンスペースの印刷後)を実行します。


2
sed -e '1,NUMBER{H;1h;d;}' -e '$G'また、移植性に動作し(いくつかのことに注意してくださいsedので、実装は、ホールドスペースで数キロバイト以上保持することはできませんNUMBERが大きすぎることはできませんが)
ステファンChazelas

@StéphaneChazelas-入力ありがとう-移植性があることは確かですが、通常は1行に1つのコマンドを入力します。複数の式の構文は常にわかりにくくなっています。たとえば、posixのドキュメントでは、「<right-brace> <newline>が前に付いているので、それによるとそうではないのsed -e '1,NUMBER{H;1h;d' -e'}' -e '$G'ですか?
don_crissti

4
通常、new -eは改行を置き換えます。d;}まだPOSIXではありませんが、移植可能です。これは、次のPOSIX仕様で修正される予定です。参照してくださいaustingroupbugs.net/view.php?id=944#c2720
ステファンChazelas

2
@don_crisstiありがとうございます!それが機能する理由の簡単な説明も含めることができれば、すばらしいでしょう。(もちろん、調べてみますが、もっと価値のある答えになるでしょう。)
Peter Uhnak

私の頭の中で、移植1,NUMBER{H;1h;d;}できないのは、オープニングブレースの直後にセミコロンがないことです。それは、SunOS 4.1のバグだったのかもしれませんが、sedその回避策は20年後も私の指に組み込まれています。
zwol

11

awkアプローチ:

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

@don_crisstiのsedアプローチに対する 1つの利点は、出力の行n数が少ない場合でも機能する(行を出力する)ことです。


ハードコーディングさ\nれたORS を置き換えて、これが他のレコードセパレーターで機能するようにすることができます(たとえば、段落を使用したい場合など)。
fedorqui

6

私はxclipそれを持っているので、これはこのようにして行うことができます:

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

ここにその説明があります:

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)

3
xclipの創造的な(誤用)の場合は+1。答えは、アクセス可能/実行中のXサーバーが必要です。
jofel

3

Perlの方法:

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

または、同じことをより不可解に記述します。

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

例えば:

$ cat file
44444
55555
11111
22222
33333

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

説明

  • -ne:入力ファイル/ストリーム-eを1行ずつ読み取り、指定されたスクリプトを各行に適用します。
  • $.<3$.は現在の行番号なので3、シフトする行数に変更します。
  • $.<3?($f.=$_):print;:これは、条件演算子は、一般的な形式がある、あるcondition ? case1 : case2、それを実行するcase1場合はconditiontrueで、case2それが偽である場合。ここでは、現在の行番号が3未満の場合、現在の行($_)を変数に追加し$f、行番号が3より大きい場合は出力します。
  • }{ print $f}{はperlの省略形ですEND{}。すべての入力行が処理された後に実行されます。この時点で、シフトしたいすべての行が収集され、そのままにしたいすべての行が印刷されるので、として保存された行を印刷し$fます。

1
ゴルフバージョンについては、いくつかのキャラクターを削除できますperl -ne '$.<3?$f.=$_:print}{print $f
123

1

POSIXを使用しexます。はい、ファイルの編集を目的としていますが、パイプラインで機能します。

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

これには、パイプラインの最初または最後に任意のコマンドを追加でき、同じように機能します。さらに、の存在を考えると、/dev/stdinPOSIXに準拠しています。

/dev/stdinがPOSIXで指定されているかどうかはわかりませんが、LinuxとMac OS Xの両方に存在することがわかります。)

これには、sedのホールドスペースを使用する場合よりも読みやすくなるという利点があります。ex「これらの行を最後に移動する」と指示するだけで実行されます。(残りのコマンドは、「バッファを出力する」と「終了する」を意味し、どちらもかなり読みやすいです。)

注意:上記のexコマンドは、入力として2行未満の場合、失敗します。

参考文献:


0

短い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='')

ファイル名を最初の引数として渡し、移動する行数を2番目の引数として渡します。

例:

$ 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

0

出力全体をメモリに保存できる場合:

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

0

GNUを必要とする別のオプションを次に示しますsed

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

-uコマンドがSTDINの3行以上を消費しないsedように、GNUをバッファーなしsedにします。コマンドの置換により空の行が削除されるため、3、3、2番目、または3、2、1番目の行が空の場合、コマンドの出力の最後に空の行は含まれません。

次のようなこともできます:

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

せずにsponge /dev/stdout|コマンド長い入力に失敗するでしょう。たとえば、入力が改行である場合に出力される場合でも、でsponge /dev/stdout置き換えることができます。または、入力の末尾から空の行を削除する場合tac|tacでも、で置き換えることができます。上記のコマンドは、入力の行数が1または2の場合、最初の行を削除します。a\ncba\nb\nc\n(x=$(cat);printf %s\\n "$x")

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.