Is there a linux command to return the last x % of a file? I know tail can return a number of lines (-n) or number of bytes (-c), but what if I wanted to get the last 25% of a file? Is there a command to do that?
3 Answers
GNU split can do pretty much what you ask; given a text file in.txt
, this will print the last quarter (part 4 out of 4) in terms of number of bytes (not lines), without splitting lines:
split -n l/4/4 in.txt
Here is the relevant documentation for split -n CHUNKS
:
CHUNKS
may be: [...]l/K/N
output Kth of N to stdout without splitting lines
In the very specific case mentioned as an example in the question,
4/4
requests the fourth quarter, or the last 25% of the input
file. For sizes that are not 1/n of the input, I do not think split
provides such a straightforward solution.
Complex bash
+ stat
+ bc
+ tail
solution for any percentage:
get_last_chunk () {
local p=$(bc <<<"scale=2; $1/100")
tail -c $(printf "%.0f" $(echo "$(stat -c%s $2) * $p" | bc)) "$2"
}
$1
and$2
- are the function's 1st and 2nd arguments respectivelyp
- variable assigned with percentage value as float number (for ex.0.14
or0.55
)stat -c%s $2
- getting the actual size of the input file in bytestail -c N $2
- getting the lastN
bytes of the file
Or use the more simplified version:
get_last_chunk () {
tail -c "$(($(stat -c%s - < "$2") * $1 / 100))" < "$2"))"
}
Signature: get_last_chunk <percent> <filename>
Sample file.txt
:
apples
oranges
bananas
cherries
Test cases:
get_last_chunk 17 file.txt
ries
get_last_chunk 77 file.txt
oranges
bananas
cherries
get_last_chunk 29 file.txt
cherries
-
-
You are probably better using
stat -c%s filename.txt
to get the filesize, rather than wc.stat
will call fstat (or similar) to ask the fs driver for the filesize, rather than scanning the whole file.– CSMJan 5, 2018 at 23:07 -
1
echo {{expression}} | bc
is a useless use ofecho
. We can insteadbc <<< {{expression}}
. Jan 6, 2018 at 2:15 -
@user137369,
<<<
means creating a temporary files filling it with the text, make that the stdin of the command and delete it. Whether it's better or worse than using the builtinecho
with a pipe is up for debate. That also adds a requirement on the system having a zsh-like shell. But here, you don't usebc
(norprintf
) at all, you can use shell arithmetic expression. Jan 6, 2018 at 10:30 -
1@user137369, here documents and here strings in
bash
are implemented with deleted temporary files like they were in the Bourne shell (which didn't have herestrings, just heredocs, herestrings come from zsh, so bash is a zsh-like shell in that regard) Jan 6, 2018 at 17:39
To get the last $1
% in terms of number of lines, portably (POSIXly):
last_percent() (
percent=${1?}; shift
ret=0
for file do
lines=$(wc -l < "$file") &&
tail -n "$((lines * percent / 100))" < "$file" || ret=$?
done
exit "$ret"
)
Example:
$ seq 12 > a; printf '%s\n' aaaaaa bbbbb cccc dd > b
$ last_percent 25 a b
10
11
12
dd
For the last $1
% in terms of number of bytes, replace wc -l
with wc -c
and tail -n
with tail -c
. Beware though that the first output line would likely be partial. On the same files as above, that would give:
$ last_percent 25 a b
11
12
c
dd
With ksh93, you could write it with only builtins and not a single fork as:
last_percent() (
percent=$1; shift
ret=0
for file do
command /opt/ast/bin/cat < "$file" <#((EOF*(100-percent)/100)) || ret=$?
done
exit "$ret"
)
Using its <#((...))
seeking operator.
The same with zsh
(except that cat
is not builtin there):
zmodload zsh/system zsh/stat
last_percent() {
local percent=$1 ret=0 file n
shift
for file do
{
sysseek -w end 0 &&
sysseek -w end ' - systell(0) * percent / 100' &&
cat
} < $file || ret=$?
done
return $ret
}
wc
to get a line/char count of the whole file, perform your own calculation, then pass the resulting value totail
less
with a per-cent offset e.g.less +p75 somefile