Delete nth line (when counted from the bottom)
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}
There are unknown number of lines in a file. How to delete nth line (when counted from the bottom) with one-liner command (you may use more than one if it is necessary) on Unix platform.
awk sed perl
New contributor
add a comment |
There are unknown number of lines in a file. How to delete nth line (when counted from the bottom) with one-liner command (you may use more than one if it is necessary) on Unix platform.
awk sed perl
New contributor
add a comment |
There are unknown number of lines in a file. How to delete nth line (when counted from the bottom) with one-liner command (you may use more than one if it is necessary) on Unix platform.
awk sed perl
New contributor
There are unknown number of lines in a file. How to delete nth line (when counted from the bottom) with one-liner command (you may use more than one if it is necessary) on Unix platform.
awk sed perl
awk sed perl
New contributor
New contributor
edited Apr 18 at 9:17
Swapnil Dhule
New contributor
asked Apr 17 at 8:53
Swapnil DhuleSwapnil Dhule
263
263
New contributor
New contributor
add a comment |
add a comment |
8 Answers
8
active
oldest
votes
To remove for example the 4th line from the bottom using sed
:
tac input | sed '4d' | tac
To overwrite the input file:
tmpfile=$(mktemp)
tac input | sed '4d' | tac > "$tmpfile" && mv "$tmpfile" input
Thanks Panki! However this won't delete line from the file. We need permanent change in file.
– Swapnil Dhule
Apr 17 at 9:09
2
Use output redirection to write to a new file, then replace the original one.
– Panki
Apr 17 at 9:17
add a comment |
Pure sed
:
If n is 1:
sed '$ d'
This is simple: if it's the last line, delete the pattern space, so it's not printed.
If n is greater than 1 (and available as
$n
):
sed "
: start
1,$((n-1)) { N; b start }
$ { t end; s/^//; D }
N
P
D
: end
"
Note
$((n-1))
is expanded by the shell beforesed
starts.
This fragment
: start
1,$((n-1)) { N; b start }
stores n-1 lines in the pattern space. If
sed
reaches the end of the input stream during this loop, the pattern space will be printed automatically (there is no n-th line from the end, no line will be deleted).
Suppose there is more input. Then, before we get to the last line, this fragment is iterated:
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
This way the pattern space is our buffer which makes the output several lines "late" according to the input.
N
from this fragment is able to read the last line of the input as well.
After the last line is read, this gets executed:
$ { t end; s/^//; D }
When this code is executed for the first time,
t
doesn't branch toend
because there was no successful substitution before. Such no-op substitutions/^//
is then performed and the first line from the pattern space is deleted (D
) without being printed. This is exactly the line you want to be deleted. SinceD
starts a new cycle, the same line of code will eventually be executed again. This timet
will branch toend
.
When
sed
reaches the end of the script, the pattern space is printed automatically. This way all remaining lines get printed.
The command will generate the same output for
n=2
(valid) andn=1
(invalid). I tried to find a single solution that works regardless of n. I failed, hence the special case when your n is 1.
add a comment |
This is tagged with sed
and awk
but the question doesn't mention these as being required for the solution. Here's a Perl filter which removes the 4th-from-last line and prints the result. The output can written to a tmp file and then used to replace the original.
perl -e '@L = <STDIN>; splice(@L,-4,1); print @L' ./lines.txt
add a comment |
if $n
hold number of line to delete
to delete a single line use
printf "$-%d+1,$-%d+1dnwqn" $n $n| ed -s file
to delete n last lines
printf "$-%d,$dnwqn" $n | ed -s file
where
$%d,$d
tell ed to delete n last lines (printf will insert n)
wq
write and quit-s
ined -s
will keep ed silent.note that no provision is made to check you have enough line to delete.
sadly range from end can't be specified in sed
...
What do you mean you can't specify ranges withsed
?sed '2,4d'
works fine on my end..
– Panki
Apr 17 at 9:20
I like to add-s
to the call toed
; quiets the output a bit.
– Jeff Schaller♦
Apr 17 at 15:45
@JeffSchaller done
– Archemar
Apr 17 at 18:36
add a comment |
Neither standard sed
nor awk
support editing in place.
For that, you better use ed(1)
or ex(1)
:
printf '$-%ddnwn' 1 | ed -s your_file
Or with here-doc
ed -s <<'EOT' your_file
$-1d
w
EOT
With advanced shells like bash
, zsh
orksh93
you can use the $'...'
syntax and here-strings:
ed -s <<<$'$-1dnw' your_file
Notice that the $
address means the last line from the file; so the index 1 there is 0-based; for the 1st line from the end replace the 1 with 0 ($-0
), for the 3nd with 2 ($-2
), etc.
Putting it in a function:
del_nth_line_from_end(){ printf '$-%ddnwn' "$(($2-1))" | ed -s "$1"; }
Instead of ed -s
you can use ex -s
or vim -es
everywhere.
add a comment |
sed
cannot calculate nth row from bottom by itself, so we need to that before, e.g. using awk
:
Delete 4th row from bottom:
delrow=$(awk -v n=4 'END { print NR-n+1 }' file)
sed -i "${delrow}d" file
add a comment |
An one-line, in-place solution:
With gawk
, this will delete the 42nd line from the bottom:
gawk -i inplace 'i==0 {if(FNR>t){t=FNR}else{i=1}} i==1 && FNR!=t-42 {print}' input input
The 42 can be replaced with any number. If 0 is used then the last line is deleted.
Notice that the input
file is specified twice. This forces two iterations of the file with gawk. In the first iteration (i==0
) the total number of lines (t
) is established. In the second iteration, the nth line from the end is not output.
The file is modified in place by using the -i
option.
add a comment |
You can combine head
and tail
to achieve this.
If the nth line from the bottom needs to be deleted
head
reads from standard input and reports to standard output total -n lines from the top
tail
reads and reports to standard output the bottom n-1 lines from standard input.- Taken as a whole, the lines are thus reported to standard output in order, with the *nth` line from the end being skipped
This solution depends on head
leaving the file offset of the open file description just after the last reported line - I believe GNU head
does indeed do this when standard input is redirected from a file
n=200; tmpfile=$(mktemp) && { head -n -$n; tail -n -$((n-1)); }<file >"$tmpfile"
&& mv -- "$tmpfile" file
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Swapnil Dhule is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f512947%2fdelete-nth-line-when-counted-from-the-bottom%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
8 Answers
8
active
oldest
votes
8 Answers
8
active
oldest
votes
active
oldest
votes
active
oldest
votes
To remove for example the 4th line from the bottom using sed
:
tac input | sed '4d' | tac
To overwrite the input file:
tmpfile=$(mktemp)
tac input | sed '4d' | tac > "$tmpfile" && mv "$tmpfile" input
Thanks Panki! However this won't delete line from the file. We need permanent change in file.
– Swapnil Dhule
Apr 17 at 9:09
2
Use output redirection to write to a new file, then replace the original one.
– Panki
Apr 17 at 9:17
add a comment |
To remove for example the 4th line from the bottom using sed
:
tac input | sed '4d' | tac
To overwrite the input file:
tmpfile=$(mktemp)
tac input | sed '4d' | tac > "$tmpfile" && mv "$tmpfile" input
Thanks Panki! However this won't delete line from the file. We need permanent change in file.
– Swapnil Dhule
Apr 17 at 9:09
2
Use output redirection to write to a new file, then replace the original one.
– Panki
Apr 17 at 9:17
add a comment |
To remove for example the 4th line from the bottom using sed
:
tac input | sed '4d' | tac
To overwrite the input file:
tmpfile=$(mktemp)
tac input | sed '4d' | tac > "$tmpfile" && mv "$tmpfile" input
To remove for example the 4th line from the bottom using sed
:
tac input | sed '4d' | tac
To overwrite the input file:
tmpfile=$(mktemp)
tac input | sed '4d' | tac > "$tmpfile" && mv "$tmpfile" input
edited Apr 17 at 18:22
glenn jackman
53.2k573114
53.2k573114
answered Apr 17 at 8:57
PankiPanki
1,030514
1,030514
Thanks Panki! However this won't delete line from the file. We need permanent change in file.
– Swapnil Dhule
Apr 17 at 9:09
2
Use output redirection to write to a new file, then replace the original one.
– Panki
Apr 17 at 9:17
add a comment |
Thanks Panki! However this won't delete line from the file. We need permanent change in file.
– Swapnil Dhule
Apr 17 at 9:09
2
Use output redirection to write to a new file, then replace the original one.
– Panki
Apr 17 at 9:17
Thanks Panki! However this won't delete line from the file. We need permanent change in file.
– Swapnil Dhule
Apr 17 at 9:09
Thanks Panki! However this won't delete line from the file. We need permanent change in file.
– Swapnil Dhule
Apr 17 at 9:09
2
2
Use output redirection to write to a new file, then replace the original one.
– Panki
Apr 17 at 9:17
Use output redirection to write to a new file, then replace the original one.
– Panki
Apr 17 at 9:17
add a comment |
Pure sed
:
If n is 1:
sed '$ d'
This is simple: if it's the last line, delete the pattern space, so it's not printed.
If n is greater than 1 (and available as
$n
):
sed "
: start
1,$((n-1)) { N; b start }
$ { t end; s/^//; D }
N
P
D
: end
"
Note
$((n-1))
is expanded by the shell beforesed
starts.
This fragment
: start
1,$((n-1)) { N; b start }
stores n-1 lines in the pattern space. If
sed
reaches the end of the input stream during this loop, the pattern space will be printed automatically (there is no n-th line from the end, no line will be deleted).
Suppose there is more input. Then, before we get to the last line, this fragment is iterated:
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
This way the pattern space is our buffer which makes the output several lines "late" according to the input.
N
from this fragment is able to read the last line of the input as well.
After the last line is read, this gets executed:
$ { t end; s/^//; D }
When this code is executed for the first time,
t
doesn't branch toend
because there was no successful substitution before. Such no-op substitutions/^//
is then performed and the first line from the pattern space is deleted (D
) without being printed. This is exactly the line you want to be deleted. SinceD
starts a new cycle, the same line of code will eventually be executed again. This timet
will branch toend
.
When
sed
reaches the end of the script, the pattern space is printed automatically. This way all remaining lines get printed.
The command will generate the same output for
n=2
(valid) andn=1
(invalid). I tried to find a single solution that works regardless of n. I failed, hence the special case when your n is 1.
add a comment |
Pure sed
:
If n is 1:
sed '$ d'
This is simple: if it's the last line, delete the pattern space, so it's not printed.
If n is greater than 1 (and available as
$n
):
sed "
: start
1,$((n-1)) { N; b start }
$ { t end; s/^//; D }
N
P
D
: end
"
Note
$((n-1))
is expanded by the shell beforesed
starts.
This fragment
: start
1,$((n-1)) { N; b start }
stores n-1 lines in the pattern space. If
sed
reaches the end of the input stream during this loop, the pattern space will be printed automatically (there is no n-th line from the end, no line will be deleted).
Suppose there is more input. Then, before we get to the last line, this fragment is iterated:
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
This way the pattern space is our buffer which makes the output several lines "late" according to the input.
N
from this fragment is able to read the last line of the input as well.
After the last line is read, this gets executed:
$ { t end; s/^//; D }
When this code is executed for the first time,
t
doesn't branch toend
because there was no successful substitution before. Such no-op substitutions/^//
is then performed and the first line from the pattern space is deleted (D
) without being printed. This is exactly the line you want to be deleted. SinceD
starts a new cycle, the same line of code will eventually be executed again. This timet
will branch toend
.
When
sed
reaches the end of the script, the pattern space is printed automatically. This way all remaining lines get printed.
The command will generate the same output for
n=2
(valid) andn=1
(invalid). I tried to find a single solution that works regardless of n. I failed, hence the special case when your n is 1.
add a comment |
Pure sed
:
If n is 1:
sed '$ d'
This is simple: if it's the last line, delete the pattern space, so it's not printed.
If n is greater than 1 (and available as
$n
):
sed "
: start
1,$((n-1)) { N; b start }
$ { t end; s/^//; D }
N
P
D
: end
"
Note
$((n-1))
is expanded by the shell beforesed
starts.
This fragment
: start
1,$((n-1)) { N; b start }
stores n-1 lines in the pattern space. If
sed
reaches the end of the input stream during this loop, the pattern space will be printed automatically (there is no n-th line from the end, no line will be deleted).
Suppose there is more input. Then, before we get to the last line, this fragment is iterated:
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
This way the pattern space is our buffer which makes the output several lines "late" according to the input.
N
from this fragment is able to read the last line of the input as well.
After the last line is read, this gets executed:
$ { t end; s/^//; D }
When this code is executed for the first time,
t
doesn't branch toend
because there was no successful substitution before. Such no-op substitutions/^//
is then performed and the first line from the pattern space is deleted (D
) without being printed. This is exactly the line you want to be deleted. SinceD
starts a new cycle, the same line of code will eventually be executed again. This timet
will branch toend
.
When
sed
reaches the end of the script, the pattern space is printed automatically. This way all remaining lines get printed.
The command will generate the same output for
n=2
(valid) andn=1
(invalid). I tried to find a single solution that works regardless of n. I failed, hence the special case when your n is 1.
Pure sed
:
If n is 1:
sed '$ d'
This is simple: if it's the last line, delete the pattern space, so it's not printed.
If n is greater than 1 (and available as
$n
):
sed "
: start
1,$((n-1)) { N; b start }
$ { t end; s/^//; D }
N
P
D
: end
"
Note
$((n-1))
is expanded by the shell beforesed
starts.
This fragment
: start
1,$((n-1)) { N; b start }
stores n-1 lines in the pattern space. If
sed
reaches the end of the input stream during this loop, the pattern space will be printed automatically (there is no n-th line from the end, no line will be deleted).
Suppose there is more input. Then, before we get to the last line, this fragment is iterated:
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
This way the pattern space is our buffer which makes the output several lines "late" according to the input.
N
from this fragment is able to read the last line of the input as well.
After the last line is read, this gets executed:
$ { t end; s/^//; D }
When this code is executed for the first time,
t
doesn't branch toend
because there was no successful substitution before. Such no-op substitutions/^//
is then performed and the first line from the pattern space is deleted (D
) without being printed. This is exactly the line you want to be deleted. SinceD
starts a new cycle, the same line of code will eventually be executed again. This timet
will branch toend
.
When
sed
reaches the end of the script, the pattern space is printed automatically. This way all remaining lines get printed.
The command will generate the same output for
n=2
(valid) andn=1
(invalid). I tried to find a single solution that works regardless of n. I failed, hence the special case when your n is 1.
answered Apr 17 at 12:03
Kamil MaciorowskiKamil Maciorowski
1,85211030
1,85211030
add a comment |
add a comment |
This is tagged with sed
and awk
but the question doesn't mention these as being required for the solution. Here's a Perl filter which removes the 4th-from-last line and prints the result. The output can written to a tmp file and then used to replace the original.
perl -e '@L = <STDIN>; splice(@L,-4,1); print @L' ./lines.txt
add a comment |
This is tagged with sed
and awk
but the question doesn't mention these as being required for the solution. Here's a Perl filter which removes the 4th-from-last line and prints the result. The output can written to a tmp file and then used to replace the original.
perl -e '@L = <STDIN>; splice(@L,-4,1); print @L' ./lines.txt
add a comment |
This is tagged with sed
and awk
but the question doesn't mention these as being required for the solution. Here's a Perl filter which removes the 4th-from-last line and prints the result. The output can written to a tmp file and then used to replace the original.
perl -e '@L = <STDIN>; splice(@L,-4,1); print @L' ./lines.txt
This is tagged with sed
and awk
but the question doesn't mention these as being required for the solution. Here's a Perl filter which removes the 4th-from-last line and prints the result. The output can written to a tmp file and then used to replace the original.
perl -e '@L = <STDIN>; splice(@L,-4,1); print @L' ./lines.txt
edited Apr 17 at 18:25
glenn jackman
53.2k573114
53.2k573114
answered Apr 17 at 13:48
Mark StosbergMark Stosberg
4,1131227
4,1131227
add a comment |
add a comment |
if $n
hold number of line to delete
to delete a single line use
printf "$-%d+1,$-%d+1dnwqn" $n $n| ed -s file
to delete n last lines
printf "$-%d,$dnwqn" $n | ed -s file
where
$%d,$d
tell ed to delete n last lines (printf will insert n)
wq
write and quit-s
ined -s
will keep ed silent.note that no provision is made to check you have enough line to delete.
sadly range from end can't be specified in sed
...
What do you mean you can't specify ranges withsed
?sed '2,4d'
works fine on my end..
– Panki
Apr 17 at 9:20
I like to add-s
to the call toed
; quiets the output a bit.
– Jeff Schaller♦
Apr 17 at 15:45
@JeffSchaller done
– Archemar
Apr 17 at 18:36
add a comment |
if $n
hold number of line to delete
to delete a single line use
printf "$-%d+1,$-%d+1dnwqn" $n $n| ed -s file
to delete n last lines
printf "$-%d,$dnwqn" $n | ed -s file
where
$%d,$d
tell ed to delete n last lines (printf will insert n)
wq
write and quit-s
ined -s
will keep ed silent.note that no provision is made to check you have enough line to delete.
sadly range from end can't be specified in sed
...
What do you mean you can't specify ranges withsed
?sed '2,4d'
works fine on my end..
– Panki
Apr 17 at 9:20
I like to add-s
to the call toed
; quiets the output a bit.
– Jeff Schaller♦
Apr 17 at 15:45
@JeffSchaller done
– Archemar
Apr 17 at 18:36
add a comment |
if $n
hold number of line to delete
to delete a single line use
printf "$-%d+1,$-%d+1dnwqn" $n $n| ed -s file
to delete n last lines
printf "$-%d,$dnwqn" $n | ed -s file
where
$%d,$d
tell ed to delete n last lines (printf will insert n)
wq
write and quit-s
ined -s
will keep ed silent.note that no provision is made to check you have enough line to delete.
sadly range from end can't be specified in sed
...
if $n
hold number of line to delete
to delete a single line use
printf "$-%d+1,$-%d+1dnwqn" $n $n| ed -s file
to delete n last lines
printf "$-%d,$dnwqn" $n | ed -s file
where
$%d,$d
tell ed to delete n last lines (printf will insert n)
wq
write and quit-s
ined -s
will keep ed silent.note that no provision is made to check you have enough line to delete.
sadly range from end can't be specified in sed
...
edited Apr 17 at 18:36
answered Apr 17 at 9:12
ArchemarArchemar
20.7k93973
20.7k93973
What do you mean you can't specify ranges withsed
?sed '2,4d'
works fine on my end..
– Panki
Apr 17 at 9:20
I like to add-s
to the call toed
; quiets the output a bit.
– Jeff Schaller♦
Apr 17 at 15:45
@JeffSchaller done
– Archemar
Apr 17 at 18:36
add a comment |
What do you mean you can't specify ranges withsed
?sed '2,4d'
works fine on my end..
– Panki
Apr 17 at 9:20
I like to add-s
to the call toed
; quiets the output a bit.
– Jeff Schaller♦
Apr 17 at 15:45
@JeffSchaller done
– Archemar
Apr 17 at 18:36
What do you mean you can't specify ranges with
sed
? sed '2,4d'
works fine on my end..– Panki
Apr 17 at 9:20
What do you mean you can't specify ranges with
sed
? sed '2,4d'
works fine on my end..– Panki
Apr 17 at 9:20
I like to add
-s
to the call to ed
; quiets the output a bit.– Jeff Schaller♦
Apr 17 at 15:45
I like to add
-s
to the call to ed
; quiets the output a bit.– Jeff Schaller♦
Apr 17 at 15:45
@JeffSchaller done
– Archemar
Apr 17 at 18:36
@JeffSchaller done
– Archemar
Apr 17 at 18:36
add a comment |
Neither standard sed
nor awk
support editing in place.
For that, you better use ed(1)
or ex(1)
:
printf '$-%ddnwn' 1 | ed -s your_file
Or with here-doc
ed -s <<'EOT' your_file
$-1d
w
EOT
With advanced shells like bash
, zsh
orksh93
you can use the $'...'
syntax and here-strings:
ed -s <<<$'$-1dnw' your_file
Notice that the $
address means the last line from the file; so the index 1 there is 0-based; for the 1st line from the end replace the 1 with 0 ($-0
), for the 3nd with 2 ($-2
), etc.
Putting it in a function:
del_nth_line_from_end(){ printf '$-%ddnwn' "$(($2-1))" | ed -s "$1"; }
Instead of ed -s
you can use ex -s
or vim -es
everywhere.
add a comment |
Neither standard sed
nor awk
support editing in place.
For that, you better use ed(1)
or ex(1)
:
printf '$-%ddnwn' 1 | ed -s your_file
Or with here-doc
ed -s <<'EOT' your_file
$-1d
w
EOT
With advanced shells like bash
, zsh
orksh93
you can use the $'...'
syntax and here-strings:
ed -s <<<$'$-1dnw' your_file
Notice that the $
address means the last line from the file; so the index 1 there is 0-based; for the 1st line from the end replace the 1 with 0 ($-0
), for the 3nd with 2 ($-2
), etc.
Putting it in a function:
del_nth_line_from_end(){ printf '$-%ddnwn' "$(($2-1))" | ed -s "$1"; }
Instead of ed -s
you can use ex -s
or vim -es
everywhere.
add a comment |
Neither standard sed
nor awk
support editing in place.
For that, you better use ed(1)
or ex(1)
:
printf '$-%ddnwn' 1 | ed -s your_file
Or with here-doc
ed -s <<'EOT' your_file
$-1d
w
EOT
With advanced shells like bash
, zsh
orksh93
you can use the $'...'
syntax and here-strings:
ed -s <<<$'$-1dnw' your_file
Notice that the $
address means the last line from the file; so the index 1 there is 0-based; for the 1st line from the end replace the 1 with 0 ($-0
), for the 3nd with 2 ($-2
), etc.
Putting it in a function:
del_nth_line_from_end(){ printf '$-%ddnwn' "$(($2-1))" | ed -s "$1"; }
Instead of ed -s
you can use ex -s
or vim -es
everywhere.
Neither standard sed
nor awk
support editing in place.
For that, you better use ed(1)
or ex(1)
:
printf '$-%ddnwn' 1 | ed -s your_file
Or with here-doc
ed -s <<'EOT' your_file
$-1d
w
EOT
With advanced shells like bash
, zsh
orksh93
you can use the $'...'
syntax and here-strings:
ed -s <<<$'$-1dnw' your_file
Notice that the $
address means the last line from the file; so the index 1 there is 0-based; for the 1st line from the end replace the 1 with 0 ($-0
), for the 3nd with 2 ($-2
), etc.
Putting it in a function:
del_nth_line_from_end(){ printf '$-%ddnwn' "$(($2-1))" | ed -s "$1"; }
Instead of ed -s
you can use ex -s
or vim -es
everywhere.
edited Apr 17 at 15:55
answered Apr 17 at 15:24
mosvymosvy
10.5k11338
10.5k11338
add a comment |
add a comment |
sed
cannot calculate nth row from bottom by itself, so we need to that before, e.g. using awk
:
Delete 4th row from bottom:
delrow=$(awk -v n=4 'END { print NR-n+1 }' file)
sed -i "${delrow}d" file
add a comment |
sed
cannot calculate nth row from bottom by itself, so we need to that before, e.g. using awk
:
Delete 4th row from bottom:
delrow=$(awk -v n=4 'END { print NR-n+1 }' file)
sed -i "${delrow}d" file
add a comment |
sed
cannot calculate nth row from bottom by itself, so we need to that before, e.g. using awk
:
Delete 4th row from bottom:
delrow=$(awk -v n=4 'END { print NR-n+1 }' file)
sed -i "${delrow}d" file
sed
cannot calculate nth row from bottom by itself, so we need to that before, e.g. using awk
:
Delete 4th row from bottom:
delrow=$(awk -v n=4 'END { print NR-n+1 }' file)
sed -i "${delrow}d" file
answered Apr 17 at 9:25
RoVoRoVo
3,980317
3,980317
add a comment |
add a comment |
An one-line, in-place solution:
With gawk
, this will delete the 42nd line from the bottom:
gawk -i inplace 'i==0 {if(FNR>t){t=FNR}else{i=1}} i==1 && FNR!=t-42 {print}' input input
The 42 can be replaced with any number. If 0 is used then the last line is deleted.
Notice that the input
file is specified twice. This forces two iterations of the file with gawk. In the first iteration (i==0
) the total number of lines (t
) is established. In the second iteration, the nth line from the end is not output.
The file is modified in place by using the -i
option.
add a comment |
An one-line, in-place solution:
With gawk
, this will delete the 42nd line from the bottom:
gawk -i inplace 'i==0 {if(FNR>t){t=FNR}else{i=1}} i==1 && FNR!=t-42 {print}' input input
The 42 can be replaced with any number. If 0 is used then the last line is deleted.
Notice that the input
file is specified twice. This forces two iterations of the file with gawk. In the first iteration (i==0
) the total number of lines (t
) is established. In the second iteration, the nth line from the end is not output.
The file is modified in place by using the -i
option.
add a comment |
An one-line, in-place solution:
With gawk
, this will delete the 42nd line from the bottom:
gawk -i inplace 'i==0 {if(FNR>t){t=FNR}else{i=1}} i==1 && FNR!=t-42 {print}' input input
The 42 can be replaced with any number. If 0 is used then the last line is deleted.
Notice that the input
file is specified twice. This forces two iterations of the file with gawk. In the first iteration (i==0
) the total number of lines (t
) is established. In the second iteration, the nth line from the end is not output.
The file is modified in place by using the -i
option.
An one-line, in-place solution:
With gawk
, this will delete the 42nd line from the bottom:
gawk -i inplace 'i==0 {if(FNR>t){t=FNR}else{i=1}} i==1 && FNR!=t-42 {print}' input input
The 42 can be replaced with any number. If 0 is used then the last line is deleted.
Notice that the input
file is specified twice. This forces two iterations of the file with gawk. In the first iteration (i==0
) the total number of lines (t
) is established. In the second iteration, the nth line from the end is not output.
The file is modified in place by using the -i
option.
answered Apr 17 at 13:02
ltn100ltn100
22326
22326
add a comment |
add a comment |
You can combine head
and tail
to achieve this.
If the nth line from the bottom needs to be deleted
head
reads from standard input and reports to standard output total -n lines from the top
tail
reads and reports to standard output the bottom n-1 lines from standard input.- Taken as a whole, the lines are thus reported to standard output in order, with the *nth` line from the end being skipped
This solution depends on head
leaving the file offset of the open file description just after the last reported line - I believe GNU head
does indeed do this when standard input is redirected from a file
n=200; tmpfile=$(mktemp) && { head -n -$n; tail -n -$((n-1)); }<file >"$tmpfile"
&& mv -- "$tmpfile" file
add a comment |
You can combine head
and tail
to achieve this.
If the nth line from the bottom needs to be deleted
head
reads from standard input and reports to standard output total -n lines from the top
tail
reads and reports to standard output the bottom n-1 lines from standard input.- Taken as a whole, the lines are thus reported to standard output in order, with the *nth` line from the end being skipped
This solution depends on head
leaving the file offset of the open file description just after the last reported line - I believe GNU head
does indeed do this when standard input is redirected from a file
n=200; tmpfile=$(mktemp) && { head -n -$n; tail -n -$((n-1)); }<file >"$tmpfile"
&& mv -- "$tmpfile" file
add a comment |
You can combine head
and tail
to achieve this.
If the nth line from the bottom needs to be deleted
head
reads from standard input and reports to standard output total -n lines from the top
tail
reads and reports to standard output the bottom n-1 lines from standard input.- Taken as a whole, the lines are thus reported to standard output in order, with the *nth` line from the end being skipped
This solution depends on head
leaving the file offset of the open file description just after the last reported line - I believe GNU head
does indeed do this when standard input is redirected from a file
n=200; tmpfile=$(mktemp) && { head -n -$n; tail -n -$((n-1)); }<file >"$tmpfile"
&& mv -- "$tmpfile" file
You can combine head
and tail
to achieve this.
If the nth line from the bottom needs to be deleted
head
reads from standard input and reports to standard output total -n lines from the top
tail
reads and reports to standard output the bottom n-1 lines from standard input.- Taken as a whole, the lines are thus reported to standard output in order, with the *nth` line from the end being skipped
This solution depends on head
leaving the file offset of the open file description just after the last reported line - I believe GNU head
does indeed do this when standard input is redirected from a file
n=200; tmpfile=$(mktemp) && { head -n -$n; tail -n -$((n-1)); }<file >"$tmpfile"
&& mv -- "$tmpfile" file
answered Apr 17 at 14:25
iruvariruvar
12.5k63063
12.5k63063
add a comment |
add a comment |
Swapnil Dhule is a new contributor. Be nice, and check out our Code of Conduct.
Swapnil Dhule is a new contributor. Be nice, and check out our Code of Conduct.
Swapnil Dhule is a new contributor. Be nice, and check out our Code of Conduct.
Swapnil Dhule is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f512947%2fdelete-nth-line-when-counted-from-the-bottom%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown