1

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?

Raffa
  • 24,905
  • 3
  • 35
  • 79
B.Tanner
  • 2,586
  • 1
  • 19
  • 31
  • [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 Answers4

1

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}"
ognjen011
  • 1,291
  • 12
  • 15
1

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
  • `printf` has the advantage of being builtin. –  Jan 21 '21 at 11:35
  • 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
  • 1
    Ah 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
0

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 to now

  • $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.

Raffa
  • 24,905
  • 3
  • 35
  • 79
0

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
B.Tanner
  • 2,586
  • 1
  • 19
  • 31