文字列のリストが与えられたら:
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-1
a
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,b
(c
つまり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
設定する最後の内部ステートメントです。残りはちょうど健康状態を確認することです。x
y
z