ファイルの行数が不明です。 Unixプラットフォームで1行のコマンド(必要に応じて複数使用することもできます)でn行目(下から数えた場合)を削除する方法。
たとえば、sed
を使用して下から4行目を削除するには:
tac input | sed '4d' | tac
入力ファイルを上書きするには:
tmpfile=$(mktemp)
tac input | sed '4d' | tac > "$tmpfile" && mv "$tmpfile" input
純粋な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 }
_
このコードが初めて実行されるとき、以前は成功した置換がなかったため、t
はend
に分岐しません。次に、このような何もしない置換_s/^//
_が実行され、パターンスペースの最初の行が印刷されずに削除されます(D
)。これはまさに削除したい行です。 D
は新しいサイクルを開始するため、同じコード行が最終的に再び実行されます。今回はt
がend
に分岐します。
sed
がスクリプトの最後に到達すると、パターンスペースが自動的に印刷されます。これにより、残りのすべての行が印刷されます。
コマンドは、_n=2
_(有効)および_n=1
_(無効)に対して同じ出力を生成します。 nに関係なく機能する単一のソリューションを見つけようとしました。私は失敗したので、nが1である特別な場合。
$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
書き込みと終了-s
ed -s
はedを黙らせます。
削除するのに十分な行があることを確認するための準備は行われていません。
残念ながら範囲末尾からはsed
で指定できません...
これはsed
とawk
でタグ付けされていますが、質問にはこれらがソリューションに必要であると記載されていません。これは、最後から4番目の行を削除して結果を出力するPerlフィルターです。出力をtmpファイルに書き込んでから、元のファイルを置き換えるために使用できます。
Perl -e '@L = <STDIN>; splice(@L,-4,1); print @L' ./lines.txt
標準のsed
もawk
も適切な編集をサポートしていません。
そのためには、ed(1)
またはex(1)
を使用することをお勧めします。
printf '$-%dd\nw\n' 1 | ed -s your_file
またはhere-doc
ed -s <<'EOT' your_file
$-1d
w
EOT
bash
、zsh
または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
を使用できます。
sed
はそれ自体では下からn番目の行を計算できないため、前に計算する必要があります。 awk
を使用:
下から4行目を削除:
delrow=$(awk -v n=4 'END { print NR-n+1 }' file)
sed -i "${delrow}d" file
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
オプションを使用して、ファイルを適切に変更します。
head
とtail
を組み合わせてこれを実現できます。
下からnth行を削除する必要がある場合
head
は標準入力から読み取り、標準出力にレポートしますtotal -n上からの行tail
は、標準入力から下のn-1行を読み取って標準出力に報告します。この解決策は、head
に依存し、最後に報告された行の直後に開いているファイルの説明のファイルオフセットを残します-GNU head
は、標準入力がファイルからリダイレクトされるときに実際にこれを行うと思います
n=200; tmpfile=$(mktemp) && { head -n -$n; tail -n -$((n-1)); }<file >"$tmpfile" \
&& mv -- "$tmpfile" file