sedを使用してスタックなどの移動を介してユーザー入力に基づいて行を移動する方法は?

sedを使用してスタックなどの移動を介してユーザー入力に基づいて行を移動する方法は?

文字列のリストが与えられたら:

a
b
c
d
e

ユーザー入力:行4を行2に移動します。

したがって、出力は次のようになります。

a
d
b
c
e

これはどのように達成できますか?sed'sholdコマンドとパターンスペースコマンドを使用する方が良いでしょう。

答え1

1行以降4行を後ろに移動するのは、2行、3行を移動するのと同じです。今後4行目以降。

$ sed -e '2 { h; d; }' -e '3 { H; d; }' -e '4 G' file
a
d
b
c
e

つまり、行2を予約済みスペースに保存します。次に、他のすべての行H(つまり、行3のみ)のスペースを予約するために追加の項目を移動する必要があります。範囲を移動したい行に予約済みのスペースを追加しますG

5行を2行後方に移動するには(つまり、2行、3行、4行を5行後方に移動する)、2番目の式3,4として使用された範囲を使用し、最後の式の範囲を5置き換えます。4

通常、1行をa次の行に移動することb(ファイルの先頭に向かって移動する場合)は、1行を次の行にb+1移動することで表現できます。a-1a

x単に一連の行をに移動すると表現すると、その行にy前に移動した後、次のように一連の行を前に移動するコマンドをz作成できます。sed

sed -e "$x { h; d; }" -e "$((x+1)),$y { H; d; }" -e "$z G" file

たとえば、「行 4 の後に行 2 と 3 を移動します」(質問での質問です):

$ x=2 y=3 z=4; sed -e "$x { h; d; }" -e "$((x+1)),$y { H; d; }" -e "$z G" file
a
d
b
c
e

たとえば、「最初の行と2番目の行を最後に移動します。」

$ x=1 y=2 z='$'; sed -e "$x { h; d; }" -e "$((x+1)),$y { H; d; }" -e "$z G" file
c
d
e
a
b

これを使って移動することはできません。一つ2番目の式の範囲が反転して誤った出力が生成されるため、今後の手順に進みます。単一行範囲の場合は、2番目の式を無視する必要があります。それ以外の場合は$x,$y { H; $x h; d; }、最初と2番目の式をH移動しましたが(不要な1行テストと不要な行番号テストを実行しますが、後のスクリプトでは式と論理を簡素化します)、置き換えることができます。

シェルスクリプトで2つの行番号a(移動したい行)と、b後に行を挿入したい行が与えられたら、a次のことができます。

if [ "$a" -eq "$b" ] || [ "$a" -eq "$((b+1))" ]; then
    # No move
    set -- -e b
else

    if [ "$a" -lt "$b" ]; then
        # Move forwards
        x="$a" y="$a" z="$b"
    else
        # Move backwards
        x="$((b+1))" y="$((a-1))" z="$a"
    fi

    set -- -e "$x,$y { H; $x h; d; }" -e "$z G"
fi

sed "$@" file

テスト:

$ a=4 b=1 sh script
a
d
b
c
e
$ a=1 b=4 sh script
b
c
d
a
e

edエディタを使用すると、次のようにしてこれを行うことができます4m1。 「ライン1の後にライン4を移動する」:

$ printf '%s\n' 4m1 ,p Q | ed -s file
a
d
b
c
e

「行 1 を行 4 に戻る」:

$ printf '%s\n' 1m4 ,p Q | ed -s file
b
c
d
a
e

上記のシェルスクリプトの要約によると、範囲を行末a,bcつまりa,b m cの内容ed)に移動するには、次のようにしますsed

# Moves range a,b to after line c
# Corresponds to "a,b m c" in ed(1)

if [ "$a" -gt "$b" ]; then
    # Error
    echo 'Range is reversed' >&2
    exit 1
elif [ "$c" -ge "$a" ] && [ "$c" -lt "$b" ]; then
    # Error
    echo 'Can not move range to within range' >&2
    exit 1
fi

if [ "$b" -eq "$c" ] || [ "$a" -eq "$((c+1))" ]; then
    # No move
    set -- -e b
else

    if [ "$a" -lt "$c" ]; then
        # Move forwards
        x="$a" y="$b" z="$c"
    else
        # Move backwards
        x="$((c+1))" y="$((a-1))" z="$b"
    fi

    set -- -e "$x,$y { H; $x h; d; }" -e "$z G"

fi

sed "$@" file

この答えで詳しく扱う他の部分と同様に、スクリプトの必須部分は移動方向に基づいて、およびをif設定する最後の内部ステートメントです。残りはちょうど健康状態を確認することです。xyz

答え2

ただawkを使用してください。すべてのUnixシステムのすべてのシェルでawkを使用してください。

$ awk 'NR==FNR{a[NR]=$0; next} FNR==1{tmp=a[4]; a[4]=a[2]; a[2]=tmp} {print a[FNR]}' file file
a
d
c
b
e

または、複数の行を置き換えるには、置換する行番号のペアswap[]で次の配列を入力します。

awk '
    BEGIN { split("2 4 3 5",swap) }
    NR==FNR { a[NR]=$0; next }
    FNR==1 {
        for ( i=1; i in swap; i+=2 ) {
            tmp = a[swap[i]]
            a[swap[i]] = a[swap[i+1]]
            a[swap[i+1]] = tmp
        }
    }
    { print a[FNR] }
' file file
a
d
e
b
c

答え3

### initialize variables
a=2
b=4
b1=$((b-1))

sedを使う

sed -n ":1
  $a,$b{
    $b1!{N;b1;}
    h;n;G
  }
  p
" file

Perlユーティリティの使用

perl -ne "$a..$b-1"' and push(@A,$_) or print($_,splice(@A))' file

awkを使う

awk -v a="$a" -v b="$b" '
a<=NR && NR<b {x[++i]=$0;next}
{
  print
  for (n=i; i>0; i--)
    print x[n-i+1]
}
' file


Pythonを使う:

python3 -c 'import sys, itertools as it

ors = rs = "\n"
inp = sys.argv.pop(1)
a,b = map(int,sys.argv[1:])

chomp = lambda x: x.rstrip(rs)

with open(inp) as g:
  f = map(chomp,g)

  for nr,_ in enumerate(f,1):
    if nr < a: print(_)
    else: break

  G = list(it.islice(f,0,b-a))
  print(G.pop(),_,*G,sep=ors)

  for _ in f: print(_)

' file "$a" "$b"

GNUデスクトップ電卓ユーティリティの使用:

< file sed 's/.*/[&]/' |
 dc -e "[q]se
[?pc   l.1+ds. $a>p]sp
[?     l.1+ds. $b>q]sq
[LMpc  l*1-ds*  1<r]sr
[?z0=e pc      z0=?]s?
[SM z0<w]sw
[
  1s.   # initialize line counter
  lpx   # print lines before 2
  lqx   # store on stack lines 2..4
  ?zs*p # save stack size n print nxt 
  lwx   # save stack onto register 4..2
  lrx   # print register contents 2..2
  l?x   # print remaining lines
]s@
l@x
"

関連情報