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](https://www.gnu.org/software/coreutils/manual/html_node/Formatting-file-timestamps.html#Formatting-file-timestamps) 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,291
- 12
- 15
-
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 appropriate `date` command. – B.Tanner Jan 21 '21 at 11:06
-
@B.Tanner think `x=$(($(printf '%(%s)T') - e))` is a better choice than `date` – Jan 21 '21 at 11:20
-
-
On my system (18.04 kubuntu) `which printf` shows an execuable at /usr/bin/printf, and `which date` shows an executable at /bin/date, whereas `which cd` and `which 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')` vs `time (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 - $6will 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.
- 24,905
- 3
- 35
- 79
-
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,586
- 1
- 19
- 31