web-dev-qa-db-ja.com

n行目を削除(下から数えた場合)

ファイルの行数が不明です。 Unixプラットフォームで1行のコマンド(必要に応じて複数使用することもできます)でn行目(下から数えた場合)を削除する方法。

8
Swapnil Dhule

たとえば、sedを使用して下から4行目を削除するには:

tac input | sed '4d' | tac

入力ファイルを上書きするには:

tmpfile=$(mktemp)
tac input | sed '4d' | tac > "$tmpfile" && mv "$tmpfile" input
17
Panki

純粋なsed

  • nが1の場合:

    _sed '$ d'
    _

    これは簡単です。それが最後の行の場合、パターンスペースを削除して、印刷されないようにします。

  • nが1より大きい場合(および_$n_として使用可能):

    _sed "
       : start
       1,$((n-1)) { N; b start }
       $ { t end; s/^//; D }
       N
       P
       D
       : end
     "
    _

    $((n-1))は、sedが開始する前にシェルによって展開されます。

    この断片

    _: start
    1,$((n-1)) { N; b start }
    _

    n-1行をパターンスペースに格納します。このループ中にsedが入力ストリームの最後に到達すると、パターンスペースが自動的に印刷されます(最後からn番目の行はなく、行は削除されません)。

    さらに入力があると仮定します。次に、最後の行に到達する前に、このフラグメントが反復されます。

    _N   # read the next line of input and append it to the pattern space
    P   # print the first line from the pattern space
    D   # delete the first line from the pattern space and start a new cycle
    _

    このように、パターンスペースは、入力に応じて出力を数行遅らせるバッファです。このフラグメントのNは、入力の最後の行も読み取ることができます。

    最後の行が読み込まれた後、これが実行されます:

    _$ { t end; s/^//; D }
    _

    このコードが初めて実行されるとき、以前は成功した置換がなかったため、tendに分岐しません。次に、このような何もしない置換_s/^//_が実行され、パターンスペースの最初の行が印刷されずに削除されます(D)。これはまさに削除したい行です。 Dは新しいサイクルを開始するため、同じコード行が最終的に再び実行されます。今回はtendに分岐します。

    sedがスクリプトの最後に到達すると、パターンスペースが自動的に印刷されます。これにより、残りのすべての行が印刷されます。

    コマンドは、_n=2_(有効)および_n=1_(無効)に対して同じ出力を生成します。 nに関係なく機能する単一のソリューションを見つけようとしました。私は失敗したので、nが1である特別な場合。

5

$n削除する行数を保持

単一行を削除するには

printf "\$-%d+1,\$-%d+1d\nwq\n" $n $n| ed -s file

最後のn行を削除するには

printf "\$-%d,\$d\nwq\n" $n | ed -s file

どこ

  • \$%d,\$d edに最後のn行を削除するように指示します(printfはnを挿入します)
  • wq書き込みと終了
  • -sed -sはedを黙らせます。

  • 削除するのに十分な行があることを確認するための準備は行われていません。

残念ながら範囲末尾からsedで指定できません...

3
Archemar

これはsedawkでタグ付けされていますが、質問にはこれらがソリューションに必要であると記載されていません。これは、最後から4番目の行を削除して結果を出力するPerlフィルターです。出力をtmpファイルに書き込んでから、元のファイルを置き換えるために使用できます。

 Perl -e '@L = <STDIN>; splice(@L,-4,1); print @L' ./lines.txt
2
Mark Stosberg

標準のsedawkも適切な編集をサポートしていません。

そのためには、ed(1)またはex(1)を使用することをお勧めします。

printf '$-%dd\nw\n' 1 | ed -s your_file

またはhere-doc

ed -s <<'EOT' your_file
$-1d
w
EOT

bashzshまたはksh93などの高度なシェルを使用すると、$'...'構文とhere-stringsを使用できます。

ed -s <<<$'$-1d\nw' your_file 

$アドレスはファイルの最後の行を意味することに注意してください。したがって、インデックス1がありますベース;最後から1行目は1を0($-0)に、3行目は2($-2)に置き換えます。

関数に入れる:

del_nth_line_from_end(){ printf '$-%dd\nw\n' "$(($2-1))" | ed -s "$1"; }

ed -sの代わりに、どこでもex -sまたはvim -esを使用できます。

1
mosvy

sedはそれ自体では下からn番目の行を計算できないため、前に計算する必要があります。 awkを使用:

下から4行目を削除:

delrow=$(awk -v n=4 'END { print NR-n+1 }' file)
sed -i "${delrow}d" file
0
pLumo

1行のインプレースソリューション:

gawkを使用すると、下から42行目が削除されます。

gawk -i inplace 'i==0 {if(FNR>t){t=FNR}else{i=1}} i==1 && FNR!=t-42 {print}' input input

42は任意の数に置き換えることができます。 0を使用すると、最後の行が削除されます。

inputファイルが2回指定されていることに注意してください。これにより、gawkでファイルが2回反復されます。最初の反復(i==0)では、行の総数(t)が確立されます。 2回目の繰り返しでは、最後からn行目は出力されません。

-iオプションを使用して、ファイルを適切に変更します。

0
ltn100

headtailを組み合わせてこれを実現できます。

下からnth行を削除する必要がある場合

  1. headは標準入力から読み取り、標準出力にレポートしますtotal -n上からの行
  2. tailは、標準入力から下のn-1行を読み取って標準出力に報告します。
  3. したがって、全体として、行は標準出力に順番に報告され、最後から* n番目の行はスキップされます

この解決策は、headに依存し、最後に報告された行の直後に開いているファイルの説明のファイルオフセットを残します-GNU headは、標準入力がファイルからリダイレクトされるときに実際にこれを行うと思います

n=200; tmpfile=$(mktemp) && { head -n -$n; tail -n -$((n-1)); }<file >"$tmpfile" \
    && mv -- "$tmpfile" file
0
iruvar