web-dev-qa-db-ja.com

特定の行をある位置から別の位置に移動します

私はubuntu端末を使用しています。ファイルの特定の行(11番目の位置)を最初の行に移動してから、最終結果を新しいファイルに転送する必要があります。元のファイルには数百行が含まれています。
これまでのところ、sedコマンドツールを使用しようとしましたが、希望どおりの結果が得られません。これは私がこれまでに得たものです:

[mission 09] $ sed -n -e '1p' -e '11p' bonjour > bonjour2

ただし、新しいファイルの1行目と11行目のみが表示されます。しかし、新しいファイルには、元の行の残りの部分と一緒に、修正された位置が必要です。

入力:

English: Hello 
Turkish: Marhaba 
Italian: Ciao 
German: Hallo 
Spanish: Hola 
Latin: Salve 
Greek: chai-ray 
Welsh: Helo 
Finnish: Hei 
Breton: Demat 
French: Bonjour 

必要な出力

French: Bonjour 
English: Hello 
Turkish: Marhaba 
Italian: Ciao 
German: Hallo 
Spanish: Hola 
Latin: Salve 
Greek: chai-ray 
Welsh: Helo 
Finnish: Hei 
Breton: Demat 

なにか提案を ?

4
ForzaTekk
sed -n '1h;2,10H;11G;11,$p' 

最初の行、新しい行のためにhをコピーし、10までHを追加します。

At 11行目、ホールドスペースを取得します

From 11終了して印刷します。


]#  sed -n '1h;2,10H;11G;11,$p' bonj 
French: Bonjour 
English: Hello 
Turkish: Marhaba 
Italian: Ciao 
German: Hallo 
Spanish: Hola 
Latin: Salve 
Greek: chai-ray 
Welsh: Helo 
Finnish: Hei 
Breton: Demat 

これはより良いです:

]# seq 20 | sed -n '1h;2,10H;11G;11,$p' 
11
1
2
3
4
5
6
7
8
9
10
12
13
14
15
16
17
18
19
20

私はあなたの例を取ります:

]# sed -e 1p -e 11p -n bonj 
English: Hello 
French: Bonjour 

...とともに -n最後に切り替えて、両方の式の数を表示します。

私も持っています-n、 その後 1h;2,10H、これは1,10H、これは行番号の範囲であり、 "hold"(保存)コマンドです。まだ何も印刷されません。

11,$pは別の範囲です。 11行目には、ホールドから戻された「11G」(つまり1-10)が出力され、11行目に追加されます。

$までの12行目は、-n


2つ作ろう-e あなたのような:

sed -n -e '1h;2,10H' -e '11G;11,$p'

1,10からホールド、11から$がプリント。


行11では、最初にGがあり、次にpです。それは重要です:

]# seq 20 | sed -n -e '1h;2,10H' -e '11,$p;11G'
11
12
13
14
15
16
17
18
19
20

ここで、行12は、印刷後に行11が取得したものを一掃します。


paramsを持つ関数として

常に11行目は、 "putfirst"関数で退屈です。

]# declare -f putfirst 
putfirst () 
{ 
    e="1h;2,$(($1-1))H;${1}G;${1},\$p";
    sed -ne "$e" $2
}

2つのステップ:文字列の生成、次にsed呼び出し。 「$」には2つの意味があります。「p」は変数ではありません!

これは、機能する最小の数です。

]# seq 7 | putfirst 3 
3
1
2
4
5
6
7

または、元の「bonj」ファイルを使用します。

]# putfirst 4 bonj | putfirst 6 |head -4
Latin: Salve 
German: Hallo 
English: Hello 
Turkish: Marhaba 

これは2つのsedが連続していますが、現在は2つの操作を実行しています。


Perl

Perl -ne '$n++; $h.=$_ if $n<11; print $_.$h if $n==11; print if $n>11' <(seq 20)

そして、ファイル名を受け取り、オプションを必要としないスクリプトとして:

$want=11 ;
while (<>) {
    $n++ ;
    if ($n < $want)            # before $want: store line
        { $lowlines .= $_ ; 
          next }               # next line (avoids 'else')  
    if ($n == $want)           # at line $want: append stored lines to $_    
        { $_ .= $lowlines }   
    print ;                    # print $_ for $n not less than $want
}

[〜#〜] awk [〜#〜](編集者ではなく、エドから盗まれた!))

NR < 11 { 
    buf[NR] = $0; next 
}
NR >=11 {
    print
    if (NR == 11) {
        for (i=1; i<11; i++) { print buf[i] }
    }
}

NRをインクリメントする代わりにnを使用して、フローをより明示的にしました。同じ「トリック」:nextはダウンストリームを簡素化します。


Perl -n

$n++ ;
$tmp = $tmp . $_  if $n < 11  ; 
print  $_ . $tmp  if $n == 11 ;
print  $_         if $n > 11  ;

これが最適な形式です。対称。

3
rastafile

edを使用する(sedおよびgrepの派生元である行エディター):

printf '%s\n' '11m0' 'w bonjour2' 'q' | ed -s bonjour

これにより、編集コマンド11m0がファイルに適用され、11行目が最初の行の前に移動します。次に、結果のドキュメントをファイルbonjour2に書き込み、終了します。

または:

printf '%s\n' '11m0' ',p' 'Q' | ed -s bonjour >bonjour2

... edコマンドを使用して特定のファイルに書き込む代わりに、ファイル全体を標準出力に出力します。結果は新しいファイル名にリダイレクトされます。 ,pコマンド(1,$pの略)は、ドキュメント全体を標準出力に出力し、Qは強制的に終了します(ドキュメントが変更されていても)。

ドキュメントを変更する(つまり、元のファイルを変更する)には、結果をファイル自体に書き戻します。

printf '%s\n' '11m0' 'wq' | ed -s bonjour

上記のバリエーションのいずれかの実行例:

$ printf '%s\n' '11m0' ',p' 'Q' | ed -s bonjour >bonjour2
$ cat bonjour2
French: Bonjour
English: Hello
Turkish: Marhaba
Italian: Ciao
German: Hallo
Spanish: Hola
Latin: Salve
Greek: chai-ray
Welsh: Helo
Finnish: Hei
Breton: Demat

last行を常に先頭に移動するには、$m0の代わりに11m0を使用します。文字列French:で始まる行を常に移動するには、/^French:/m0を使用します。

9
Kusalananda

私が正しく理解していれば、11行目を最初の行に移動したいだけです。

だからあなたのオリジナルが次のようになった場合

Original line 1
Original line 2
...
Original line 18
Original line 19
Original line 20

その後、結果は

Original line 11
Original line 1
Original line 2
...
Original line 9
Original line 10
Original line 12
Original line 13
...
Original line 19
Original line 20

これは、ファイルを通過するtwoパスで実現できますが、最初のパスはショートカットにすることができるので、それほどひどくはありません。

( sed -n '11 {p;q}' file ; sed 11d file ) > newfile

基本的に、最初のsedコマンドは、11行目のみを出力してから終了します。 2番目のsedコマンドは、すべての行を出力しますexcept 11行目。

その結果、事実上、11行目が新しいファイルの前に移動されます。

3
Stephen Harris

以下の私の元の回答を投稿した後、配列を持つことはやりすぎであり、本当に必要なのは次のものだけであることに気づきました:

awk '
NR < 11 { buf = buf $0 ORS; next }
{
    print
    printf "%s", buf
    buf = ""
}
' file > new_file

例えば。:

$ seq 15 | awk '
NR < 11 { buf = buf $0 ORS; next }
{
    print
    printf "%s", buf
    buf = ""
}
'
11
1
2
3
4
5
6
7
8
9
10
12
13
14
15

バッファーにスカラー変数の代わりに配列を使用する元の答え:

awk '
NR < 11 { buf[++bufSz] = $0; next }
{
    print
    for (i=1; i<=bufSz; i++) {
        print buf[i]
    }
    bufSz = 0
}
' file > new_file

例えば。:

$ seq 15 | awk '
NR < 11 { buf[++bufSz] = $0; next }
{
    print
    for (i=1; i<=bufSz; i++) {
        print buf[i]
    }
    bufSz = 0
}
'
11
1
2
3
4
5
6
7
8
9
10
12
13
14
15
2
Ed Morton

POSIX sed

$ sed -e '1,11!b' -e '11!H;1h;11!d;G' file

Perlの使用:

$ Perl -lne '
   Push(@A,$_),next if $. < 11;
   print for $_, splice @A;
' file

bashの使用:

$ (head -n 11 - | tee log | tail -n 1 -; head -n 10 log; cat -;) < file
0
Rakesh Sharma

投稿されたものより単純なsed(POSIX、ラベルなし):

seq 20 | sed '1,10{H;1h;d;};11G'

つまり、1行目から10行目で、ホールドスペースをそれらの行で埋めます(印刷しません)。 11行目で、保留スペースを元に戻し、それ以降、sedにデフォルトを実行させます。各行を出力するだけです。 11行目以降のスクリプトは何もしないことに注意してください。デフォルトではsedのままにしてください。おそらくより高速です。

Awkで:

awk 'NR <11{ hold=hold RS $0 }
     NR==11{ print $0, hold  }
     NR >11
    ' file

1つのライナーとして:

$ seq 15 | awk 'NR<11{hold=hold RS $0}NR==11{print $0,hold}NR>11'
11 
1
2
3
4
5
6
7
8
9
10
12
13
14
15
0
Isaac