how do I check in bash whether a file was created more than x time ago? - linux

I want to check in linux bash whether a file was created more than x time ago.
let's say the file is called text.txt and the time is 2 hours.
if [ what? ]
then
echo "old enough"
fi

Only for modification time
if test `find "text.txt" -mmin +120`
then
echo old enough
fi
You can use -cmin for change or -amin for access time. As others pointed I don’t think you can track creation time.

I always liked using date -r /the/file +%s to find its age.
You can also do touch --date '2015-10-10 9:55' /tmp/file to get extremely fine-grained time on an arbitrary date/time.

Using the stat to figure out the last modification date of the file, date to figure out the current time and a liberal use of bashisms, one can do the test that you want based on the file's last modification time1.
if [ "$(( $(date +"%s") - $(stat -c "%Y" "$somefile") ))" -gt "7200" ]; then
echo "'$somefile' is older then 2 hours"
fi
While the code is a bit less readable then the find approach, I think its a better approach then running find to look at a file you already "found". Also, date manipulation is fun ;-)
As Phil correctly noted creation time is not recorded, but use %Z instead of %Y below to get "change time" which may be what you want.
[Update]
For mac users, use stat -f "%m" "$somefile" instead of the Linux specific syntax above

Creation time isn't stored.
What are stored are three timestamps (generally, they can be turned off on certain filesystems or by certain filesystem options):
Last access time
Last modification time
Last change time
a "Change" to the file is counted as permission changes, rename etc. While the modification is contents only.

Although ctime isn't technically the time of creation, it quite often is.
Since ctime it isn't affected by changes to the contents of the file, it's usually only updated when the file is created. And yes - I can hear you all screaming - it's also updated if you change the access permissions or ownership... but generally that's something that's done once, usually at the same time you put the file there.
Personally I always use mtime for everything, and I imagine that is what you want. But anyway... here's a rehash of Guss's "unattractive" bash, in an easy to use function.
#!/bin/bash
function age() {
local filename=$1
local changed=`stat -c %Y "$filename"`
local now=`date +%s`
local elapsed
let elapsed=now-changed
echo $elapsed
}
file="/"
echo The age of $file is $(age "$file") seconds.

The find one is good but I think you can use anotherway, especially if you need to now how many seconds is the file old
date -d "now - $( stat -c "%Y" $filename ) seconds" +%s
using GNU date

Consider the outcome of the tool 'stat':
File: `infolog.txt'
Size: 694 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 11635578 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ fdr) Gid: ( 1000/ fdr)
Access: 2009-01-01 22:04:15.000000000 -0800
Modify: 2009-01-01 22:05:05.000000000 -0800
Change: 2009-01-01 22:05:05.000000000 -0800
You can see here the three dates for Access/modify/change. There is no
created date. You can only really be sure when the file contents were
modified (the "modify" field) or its inode changed (the "change"
field).
Examples of when both fields get updated:
"Modify" will be updated if someone concatenated extra information to
the end of the file.
"Change" will be updated if someone changed permissions via chmod.

I use
file_age() {
local filename=$1
echo $(( $(date +%s) - $(date -r $filename +%s) ))
}
is_stale() {
local filename=$1
local max_minutes=20
[ $(file_age $filename) -gt $(( $max_minutes*60 )) ]
}
if is_stale /my/file; then
...
fi

Related

modifying atime to check if file has been modified within last 24 hours

#!/bin/bash
cd /home/pi/cc/uvreadings
while true
do
ATIME=`stat -c %Z /home/pi/cc/uvreadings/uvreadings.log`
if [[ "$ATIME" < "$LTIME" ]]
then
echo "log file not updated for +24 hours"
else
echo "log file WAS updated in last +24 hours"
fi
sleep 10
done
I am trying to check if a file has been modified in the last 24 hours by comparing atime to Ltime
atime will always be less than ltime
so can i modify the statement
if [[ "$ATIME" < "$LTIME" ]]
to
if [[ "$ATIME +1 day" < "$LTIME" ]]`
of is the a better way to achieve this
thanks for any advice
First of all, there are (at least) three different timestamps on a file, and you seem to have them mixed up. You talk about atime, the %Z option instead gives you ctime, and it sounds like what you want is actually mtime: the time at which the file's contents last changed. You can get mtime with stat -c %Y.
To see if it was less than one day ago, you can indeed add one day to it; but the time is in seconds, so you need to add 24*60*60 seconds. The easiest way to do that is probably with arithmetic expansion. It could look something like this:
if [[ $((MTIME + (24 * 60 * 60))) > $LTIME ]] # modified in the last day
I assume you wrote code to get the current time and store it in LTIME, even though you didn't show that code in your question. It has to go inside the loop, of course. One way to do that would be
LTIME=`date +%s`

Changing File name Dynamically in linux bash

I want to change a filename "Domain_20181012230112.csv" to "Domain_12345_20181012230112.csv" where "Domain" and "12345" are constants while 20181012230112 is always gonna change but with fix length. In bash how can I do this
If all you want is to replace Domain_ with Domain_12345_, then just do
for file in Domain_*;
do
mv "$file" "${file/Domain_/Domain_12345_}"
done
You can make it even shorter if you know that there will only be one underscore:
...
mv "$file" "${file/_/_12345_}"
...
See string substitutions for more info.
You can use mv in a for loop, like this:
for file in Domain_??????????????.csv ; do ts=`echo ${file} | cut -c8-21`; mv ${file} Domain_12345_${ts}.csv; done
Given the one file of your example, this will essentially execute this command
mv Domain_20181012230112.csv Domain_12345_20181012230112.csv
You can simply use the date command to get the date and time information you want
date '+%Y-%m-%d %H:%M:%S'
# 2018-10-26 10:25:47
To then use the result within the filename, you can put it in `` to evaluate it inline, for example you can run
echo "Domain_12345_`date '+%Y-%m-%d %H:%M:%S'`"
# Domain_12345_2018-10-26 10:29:17
You can use the date's man page to figure out the option for milliseconds to add es well.
man date
There are different options like %m and %d for example that always have leading zeroes if necessary, so the file name length stays constant.
To then rename the file you can use the mv (move) command
mv "Domain_20181012230112.csv" "Domain_12345_`date '+%Y-%m-%d %H:%M:%S'`.csv"
Good luck with the rest of the exercise!

Integer expression expected when calculating date difference

I have this script I am building and need a bit of help. I want to use as minimal lines of code as possible. However what I want the script to do is the following.
Backup a specified file such as authlog into a new file with the keywords I specify.
However. I also want it to back up the file if current backed up file is older than 2+ days ago and if it is newer. Leave it alone. if (file) is older than +2 days old delete it and replace it with an updated one.
I'm getting integer expression expected and I'm not sure how to fix it
#!/bin/bash
authlog=/home/(myhomedir)/logs/backups/authlog-${current_date}
backup="$(cat /var/log/auth.log | grep -e failed -e invalid > /home/(myhomedir)/logs/backup)"
dayold=$(date -d '1 day ago' "+%Y-%m-%d")
current_date=$(date +"%m_%d_%Y")
if [ $authlog -lt $dayold ] ;
then ${backup}
else
echo $authlog is newer than $current_date $authlog has not been updated
if [ $authlog -eq ${current_date} ]
then "$(rm {authlog})"
fi
fi
You can't use -lt to compare non-numeric strings. This is why you're getting "integer expression expected". You're trying to compare a filename to a date string.
The easiest way to compare dates is by converting them to seconds since epoch (Jan 1, 1970 00:00:00 UTC). As an example, suppose I have a file called foo.bar and I want to check if it was last modified 3 or more days ago:
filetime=$(stat -c %Y foo.bar)
three_days_ago=$(date -d "3 days ago" +%s)
if [ $filetime -lt $three_days_ago ]; then
echo "foo.bar was last modified more than 3 days ago"
fi
Also, your authlog variable does not contain a date. I'm assuming there's a missing step that you forgot.

Bash command to archive files daily based on date added

I have a suite of scripts that involve downloading files from a remote server and then parsing them. Each night, I would like to create an archive of the files downloaded that day.
Some constraints are:
Downloading from a Windows server to an Ubuntu server.
Inability to delete files on the remote server.
Require the date added to the local directory, not the date the file was created.
I have deduplication running at the downloading stage; however, (using ncftp), the check involves comparing the remote and local directories. A strategy is to create a new folder each day, download files into it and then tar it sometime after midnight. A problem arises in that the first scheduled download on the new day will grab ALL files on the remote server because the new local folder is empty.
Because of the constraints, I considered simply archiving files based on "date added" to a central folder. This works very well using a Mac because HFS+ stores extended metadata such as date created and date added. So I can combine a tar command with something like below:
mdls -name kMDItemFSName -name kMDItemDateAdded -raw *.xml | \
xargs -0 -I {} echo {} | \
sed 'N;s/\n/ /' | \
but there doesn't seem to be an analogue under linux (at least not with EXT4 that I am aware of).
I am open to any form of solution to get around doubling up files into a subsequent day. The end result should be an archives directory full of tar.gz files looking something like:
files_$(date +"%Y-%m-%d").tar.gz
Depending on the method that is used to backup the files, the modified or changed date should reflect the time it was copied - for example if you used cp -p to back them up, the modified date would not change but the changed date would reflect the time of copy.
You can get this information using the stat command:
stat <filename>
which will return the following (along with other file related info not shown):
Access: 2016-05-28 20:35:03.153214170 -0400
Modify: 2016-05-28 20:34:59.456122913 -0400
Change: 2016-05-29 01:39:52.070336376 -0400
This output is from a file that I copied using cp -p at the time shown as 'change'.
You can get just the change time by calling stat with a specified format:
stat -c '%z' <filename>
2016-05-29 01:39:56.037433640 -0400
or with capital Z for that time in seconds since epoch. You could combine that with the date command to pull out just the date (or use grep, etc)
date -d "`stat -c '%z' <filename>" -I
2016-05-29
The command find can be used to find files by time frame, in this case using the flags -cmin 'changed minutes', -mmin 'modified minutes', or unlikely, -amin 'accessed minutes'. The sequence of commands to get the minutes since midnight is a little ugly, but it works.
We have to pass find an argument of "minutes since a file was last changed" (or modified, if that criteria works). So first you have to calculate the minutes since midnight, then run find.
min_since_mid=$(echo $(( $(date +%s) - $(date -d "(date -I) 0" +%s) )) / 60 | bc)
Unrolling that a bit:
$(date +%s) == seconds since epoch until 'now'
"(date -I) 0" == todays date in format "YYYY-MM-DD 0" with 0 indicating 0 seconds into the day
$(date -d "(date -I 0" +%s)) == seconds from epoch until today at midnight
Then we (effectively) echo ( $now - $midnight ) / 60 to bc to convert the results into minutes.
The find call is passed the minutes since midnight with a leading '-' indicating up to X minutes ago. A'+' would indicate X minutes or more ago.
find /path/to/base/folder -cmin -"$min_since_mid"
The actual answer
Finally to create a tgz archive of files in the given directory (and subdirectories) that have been changed since midnight today, use these two commands:
min_since_mid=$(echo $(( $(date +%s) - $(date -d "(date -I) 0" +%s) )) / 60 | bc)
find /path/to/base/folder -cmin -"${min_since_mid:-0}" -print0 -exec tar czvf /path/to/new/tarball.tgz {} +
The -print0 argument to find tells it to delimit the files with a null string which will prevent issues with spaces in names, among other things.
The only thing I'm not sure on is you should use the changed time (-cmin), the modified time (-mmin) or the accessed time (-amin). Take a look at your backup files and see which field accurately reflects the date/time of the backup - I would think changed time, but I'm not certain.
Update: changed -"$min_since_mid" to -"${min_since_mid:-0}" so that if min_since_mid isn't set you won't error out with invalid argument - you just won't get any results. You could also surround the find with an if statement to block the call if that variable isn't set properly.

Get mtime of specific file using Bash?

I am well aware of being able to do find myfile.txt -mtime +5 to check if my file is older than 5 days or not. However I would like to fetch mtime in days of myfile.txt and store it into a variable for further usage. How would I do that?
stat can give you that info:
filemtime=$(stat -c %Y myfile.txt)
%Y gives you the last modification as "seconds since The Epoch", but there are lots of other options; more info. So if the file was modified on 2011-01-22 at 15:30 GMT, the above would return a number in the region of 1295710237.
Edit: Ah, you want the time in days since it was modified. That's going to be more complicated, not least because a "day" is not a fixed period of time (some "days" have only 23 hours, others 25 — thanks to daylight savings time).
The naive version might look like this:
filemtime=$(stat -c %Y "$1")
currtime=$(date +%s)
diff=$(( (currtime - filemtime) / 86400 ))
echo $diff
...but again, that's assuming a day is always exactly 86,400 second long.
More about arithmetic in bash here.
The date utility has a convenient switch for extracting the mtime from a file, which you can then display or store using a format string.
date -r file "+%F"
# 2021-01-12
file_mtime=$(date -r file "+%F")
See man date, the output of date is controlled by a format string beginning with "+"
Useful format strings for comparing many dates might include:
"+%j": day of year
"+%s": unix epoch time
Arithmetic with dates is a bit of a pain in bash, so if you need relative time that will work in all corner cases, you may be better off with another language.
AGE=$(perl -e 'print -M $ARGV[0]' $file)
will set $AGE to the age of $file in days, as Perl's -M operator handles the stat call and the conversion to days for you.
The return value is a floating-point value (e.g., 6.62849537 days). Add an int to the expression if you need to have an integer result
AGE=$(perl -e 'print int -M $ARGV[0]' $file)
Ruby and Python also have their one-liners to stat a file and return some data, but I believe Perl has the most concise way.
I this the answer?
A=$(stat -c "%y" myfile.txt)
look at stat-help
stat --help
Usage: stat [OPTION]... FILE...
Display file or file system status.
[...]
-c --format=FORMAT use the specified FORMAT instead of the default;
output a newline after each use of FORMAT
[...]
The valid format sequences for files
[...]
%y Time of last modification, human-readable
%Y Time of last modification, seconds since Epoch
[...]

Resources