If I last modified a file 5 minutes ago, is it possible to make ls-l
output something like "5 mins" instead of the actual date/time?
-
Here you will find some useful information. – mook765 Jan 25 '21 at 18:28
4 Answers
I use stat to get metadata info on the files. Some examples:
stat -c $'%y\t%n' * | sort -n
Output looks like this:
2020-01-27 11:52:25.681249958 +0200 CHANGELOG.md
Then to lookup a single file
stat CHANGELOG.md
and output looks like this:
File: CHANGELOG.md
Size: 94 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 6029378 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 998/example) Gid: ( 998/example)
Access: 2020-11-12 17:47:34.768793021 +0200
Modify: 2020-01-27 11:52:25.681249958 +0200
Change: 2020-11-12 17:47:02.282093672 +0200
Birth: -
Otherwise you might need a small bash script to show you the difference between when the file was created and current time.
LastUpdate="$(stat -c %Y myfile)"
now="$(date +%s)"
let diff="${now}-${lastUpdate}"

- 1,319
-
1
-
Only in bash 5 and later - 18.04 LTS is on bash 4.4, 20.04 LTS is bash 5. – B.Tanner Jan 21 '21 at 10:59
This is a continuation of an earlier script used for parsing
ls -l
with some enhancement:
#!/bin/bash
while read -r p c u g s e n; do
[[ $p = total ]] && {
echo "$p $c" 1>&2; continue;
}
x=$(($EPOCHSECONDS - e))
y=$((x / 60))
z=$((x % 60))
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n'
"$p"
"$c"
"$u"
"$g"
"$s"
"$y minutes"
"$z seconds"
"$n"
done < <(
ls -LApl --color=force
--time-style=+%s
--quoting-style=shell-escape "$@"
)
| column -t -R 2,5,6,7 -s $'\t' -o ' '
-
Thank you it hadn't occured to me that you could parse the output of
ls -l
in bash so easily. I already have a bash function that outputs a duration in seconds in the format I like, so I will start from this.$EPOCHSECONDS
doesn't work for me though on 18.04 (bash 4.4) but I can use the appropriatedate
command. – B.Tanner Jan 21 '21 at 11:06 -
-
-
On my system (18.04 kubuntu)
which printf
shows an execuable at /usr/bin/printf, andwhich date
shows an executable at /bin/date, whereaswhich cd
andwhich shopt
do not produce an output (and return an error code of 1). But date is 99k and printf is 51k :-O – B.Tanner Jan 21 '21 at 17:59 -
I think the order is alias, keyword, function, builtin, and file. but I'm not really sure. You can use
type printf
, builtin has the advantage of not needing any load time.time (printf '%(%s)T')
vstime (date +%s)
– Jan 21 '21 at 18:53 -
think
pico /usr/bin/which
is an ordinary shell script, only searching the PATH. – Jan 21 '21 at 19:19 -
1Ah thank you...'type'! I wonder why printf is there on $PATH as an exectable file too then? I suppose non-bash shells might not have it as a builtin? – B.Tanner Jan 21 '21 at 19:25
One way:
You can make ls
print modification time in seconds since epoch, like so:
ls -l --time-style="+%s"
Then pipe the ls
output to awk
to do the logic, like so:
ls -l --time-style="+%s" | awk -v now=$(date +%s) '{$6 = now - $6; d=int($6/60/60/24); h=int($6/60/60%24); m=int($6/60%60); s=int($6%60);$6 = "="d" Days "h" Hours "m" Mins "s" Secs ago="; print}' | column -t -s'='
Because the command line is long, here is a break-up which is basically the same but can be visually more contained:
ls -l --time-style="+%s" | \
awk -v now=$(date +%s) '{\
$6 = now - $6; \
d=int($6/60/60/24); \
h=int($6/60/60%24); \
m=int($6/60%60); \
s=int($6%60);\
$6 = "="d" Days "h" Hours "m" Mins "s" Secs ago="; print}' | column -t -s'='
-v now=$(date +%s)
will assign the current time in seconds since epoch tonow
$6 = now - $6
will calculate the time in seconds since file last modified.d=int($6/60/60/24)
will calculate the days.h=int($6/60/60%24)
will calculate remaining hours less than one day.m=int($6/60%60)
will calculate remaining minutes less than one hour.s=int($6%60)
will calculate remaining seconds less than one minute.$6 = d" Days "h" Hours "m" Mins "s" Secs ago"
will change the modification date to something like 2 Days 5 Hours 3 Mins 47 Secs ago.
Another way:
You can construct your own custom ls -l
alternative in a bash script, like so:
#!/bin/bash
for f in *
do
p=$(stat -c "%A=%h=%U=%G=%s" "$f")
m=$(date -r "$f" "+%s")
n=$(date "+%s")
l=$((n-m))
d=$((l/60/60/24))
h=$((l/60/60%24))
m=$((l/60%60))
s=$((l%60))
echo "$p= = =$d Days=$h Hours=$m Mins=$s=Secs= = =$f" | column -t -s'='
done
or make the output simpler and less time strict, like so:
#!/bin/bash
for f in *
do
p=$(stat -c "%A %h %U %G %s" "$f")
m=$(date -r "$f" "+%s")
n=$(date "+%s")
l=$((n-m))
d=$((l/60/60/24))
h=$((l/60/60%24))
m=$((l/60%60))
s=$((l%60))
if (( $d >= 1 ))
then
t="$d Days"
elif (( $h >= 1 ))
then
t="$h Hours"
elif (( $m >= 1 ))
then
t="$m Mins"
else
t="$s Secs"
fi
echo "$p= = =$t= = =$f" | column -t -s'='
done
Notice on usage:
If you save either the above command or the above script in a file in your home directory, name the file lsl.sh
and make it executable like so:
chmod +x ~/lsl.sh
you can add an alias like this:
alias lsl='bash ~/lsl.sh'
to your ~/.bashrc
file so you can afterwords run the short command:
`
lsl
from any directory and get the desired result.

- 32,237
-
Thank you... to bash or awk?! I am going to run with bash for now because I am more familiar with bash than awk. – B.Tanner Jan 21 '21 at 11:08
-
Answering my own question here because this is what I actually ended up doing.
I could not get any satisfactory results using anything that parsed the output of ls -l
into a word stream (bash
, awk
, column
etc) because a) filenames with spaces in messed up, and b) ls -l
's aligned columns messed up (for example, right-aligned filesizes). ls -l | column -t
shows both problems at once.
I will get shot down for the inefficiency in this, but this is my eventual script that uses sed
to replace parts of the output of ls -l
maintaining column width:
day0=`date +"%b %_d"`
day1=`date --date "1 day ago" +"%b %_d"`
day2=`date --date "2 days ago" +"%b %_d"`
day3=`date --date "3 days ago" +"%b %_d"`
day4=`date --date "4 days ago" +"%b %_d"`
day5=`date --date "5 days ago" +"%b %_d"`
day6=`date --date "6 days ago" +"%b %_d"`
min0=date +"%H:%M"
min1=date --date "1 minute ago" +"%H:%M"
min2=date --date "2 minutes ago" +"%H:%M"
min3=date --date "3 minutes ago" +"%H:%M"
min4=date --date "4 minutes ago" +"%H:%M"
min5=date --date "5 minutes ago" +"%H:%M"
min6=date --date "6 minutes ago" +"%H:%M"
min7=date --date "7 minutes ago" +"%H:%M"
min8=date --date "8 minutes ago" +"%H:%M"
min9=date --date "9 minutes ago" +"%H:%M"
ls -lh --color=force --quoting-style=shell-escape "$@"|sed "
/$day6 (.[0-9]:[0-9][0-9])/ s/$day6/6 days/; t
/$day5 (.[0-9]:[0-9][0-9])/ s/$day5/5 days/; t
/$day4 (.[0-9]:[0-9][0-9])/ s/$day4/4 days/; t
/$day3 (.[0-9]:[0-9][0-9])/ s/$day3/3 days/; t
/$day2 (.[0-9]:[0-9][0-9])/ s/$day2/2 days/; t
/$day1 (.[0-9]:[0-9][0-9])/ s/$day1/yesday/; t
/$day0 (.[0-9]:[0-9][0-9])/ s/$day0/ today/; T
s/$min0/ now/; t
s/$min1/1 min/; t
s/$min2/2 min/; t
s/$min3/3 min/; t
s/$min4/4 min/; t
s/$min5/5 min/; t
s/$min6/6 min/; t
s/$min7/7 min/; t
s/$min8/8 min/; t
s/$min9/9 min/; t
"
Sample output of /var/log/syslog*
:
-rw-r----- 1 syslog adm 9240 today now /var/log/syslog
-rw-r----- 1 syslog adm 1279965 today 8 min /var/log/syslog.1
-rw-r----- 1 syslog adm 51750 2 days 14:12 /var/log/syslog.2.gz
-rw-r----- 1 syslog adm 14768 3 days 10:59 /var/log/syslog.3.gz
-rw-r----- 1 syslog adm 7767 4 days 10:04 /var/log/syslog.4.gz
-rw-r----- 1 syslog adm 119295 5 days 09:49 /var/log/syslog.5.gz
-rw-r----- 1 syslog adm 33450 6 days 13:06 /var/log/syslog.6.gz
-rw-r----- 1 syslog adm 21372 Jan 25 11:12 /var/log/syslog.7.gz
(not sure why log appears here in red; it doesn't on my terminal.)
This is only used from the command line so all those date
commands don't matter, in fact I don't notice any delay at all.
Update:
time
output for `ls -l /var/log/syslog*':
real 0m0.002s
user 0m0.002s
sys 0m0.000s
time
output for for my new script:
real 0m0.019s
user 0m0.018s
sys 0m0.003s

- 2,636