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 -lin 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.$EPOCHSECONDSdoesn't work for me though on 18.04 (bash 4.4) but I can use the appropriatedatecommand. – B.Tanner Jan 21 '21 at 11:06 -
-
-
On my system (18.04 kubuntu)
which printfshows an execuable at /usr/bin/printf, andwhich dateshows an executable at /bin/date, whereaswhich cdandwhich shoptdo 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/whichis 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.
- 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