Bash script to connect to a remote server, and pull the last time a file was modified - linux

I am looking to create a bash script to query a remote server for the last time every instance of a file was modified. Each home directory has a version of this file.
For example, both owner and owner1 have a copy of foo.txt in their home directories on a remote box accessible via ssh.
/home/owner/
-rw-r--r-- 1 owner owner 3368 Jul 29 2014 foo.txt
/home/owner1/
-rw-r--r-- 1 owner1 owner1 3368 Jul 28 2014 foo.txt
I would like to output this information to a file that would look like:
User: owner Last Modified: Jul 29 2014
User: owner1 Last Modified: Jul 28 2014

You really ought to at least show that you attempted to write the script youself. Anyway, it's only a one-liner, so why quibble:
ssh remote-box 'ls -l /home/*/foo.txt'
It's not precisely the format you suggested, but it has all the information you asked for.

echo owner: `ssh owner#remote-box "date -r foo.txt"`>output.txt
echo owner1: `ssh owner1#remote-box "date -r foo.txt"`>>output.txt

The following function will print the data you're looking for:
remote_mod() {
ssh $1 ls -l $2 | awk '{ print "User: "$3" Last Modified: "$6" "$7" "$8 }'
}
This prints something like:
$ remote_mod yourmachine '~/.bashrc'
User: root Last Modified: Jun 2 15:01
You can then do this in a loop if you want to run the command against multiple remote files:
for d in owner owner1
do
remote_mod yourmachine /home/$d/foo.txt
done
The stat command will give you even more information, but it's in a more verbose format.

Here you go, maybe not exactly the output you want but I'm sure you will be able to modify the script to suit your needs. Make sure the user you ssh with have read access to the home directories.
ssh HOSTNAME "find /home/ -maxdepth 2 -name foo.txt | xargs -l -I{} bash -c '{
DIR=\$(dirname {});
LAST=\$(stat -c %y {});
echo "Dir:\${DIR} Last Modified :\${LAST}"
}'"
If the owner of the file is the "user" you want to be printed, you can simplify with :
ssh HOSTNAME "find /home/ -maxdepth 2 -name foo.txt | xargs -l -I{} bash -c '{
stat -c \"User: %U Last Modified : %y\" {};
}'"

Related

Modify the bash script so that after checking the type of the entity, it also lists the details of the input entity

PASSED=$1
if [ -f $PASSED ]; then
echo "$PASSED is a file"
ls -l $PASSED
elif [ -d $PASSED ]; then
echo "$PASSED is a directory"
ls -l $PASSED
else
"$PASSED is invalid"
fi
At the terminal when I push a file input, say demo.sh, the output is correctly printed as:
"demo.sh is a file"
rwxr-xr-x 1 system system 12 Jan 16 03:12 26 14:47 demo.sh
but for a directory, say cloud, it gives:
cloud is a directory
total 0
What should I do to rectify this?
enter image description here
As shown from the man pages for ls:
-d, --directory
list directories themselves, not their contents
With your current implementation, as you are using just -l with your directories, ls will show the contents of the directory. To list the properties of the directory only, use -d in addition to -l and so:
ls -ld $PASSED

Linux - Sum total of files in different directories

How do I calculate the sum total size of multiple files located in different directories?
I have a text file containing the full path and name of the files.
I figure a simple script using while read line and du -h might do the trick...
Example of text file (new2.txt) containing list of files to sum:
/mount/st4000/media/A/amediafile.ext
/mount/st4000/media/B/amediafile.ext
/mount/st4000/media/C/amediafile.ext
/mount/st4000/media/D/amediafile.ext
/mount/st4000/media/E/amediafile.ext
/mount/st4000/media/F/amediafile.ext
/mount/st4000/media/G/amediafile.ext
/mount/st4000/media/H/amediafile.ext
/mount/st4000/media/I/amediafile.ext
/mount/st4000/media/J/amediafile.ext
/mount/st4000/media/K/amediafile.ext
Note: the folder structure is not necessarily consecutive as in A..K
Based on the suggestion from AndreaT, adapting it slightly, I tried
while read mediafile;do du -b "$mediafile"|cut -f -1>>subtotals.txt;done<new2.txt
subtotals.txt looks like
733402685
944869798
730564608
213768
13332480
366983168
6122559750
539944960
735039488
1755005744
733478912
To add all the subtotals
sum=0; while read num; do ((sum += num)); done < subtotals.txt; echo $sum
Assuming that file input is like this
/home/administrator/filesum/cliprdr.c
/home/administrator/filesum/cliprdr.h
/home/administrator/filesum/event.c
/home/administrator/filesum/event.h
/home/administrator/filesum/main.c
/home/administrator/filesum/main.h
/home/administrator/filesum/utils.c
/home/administrator/filesum/utils.h
and the result of command ls -l is
-rw-r--r-- 1 administrator administrator 13452 Oct 4 17:56 cliprdr.c
-rw-r--r-- 1 administrator administrator 1240 Oct 4 17:56 cliprdr.h
-rw-r--r-- 1 administrator administrator 8141 Oct 4 17:56 event.c
-rw-r--r-- 1 administrator administrator 2164 Oct 4 17:56 event.h
-rw-r--r-- 1 administrator administrator 32403 Oct 4 17:56 main.c
-rw-r--r-- 1 administrator administrator 1074 Oct 4 17:56 main.h
-rw-r--r-- 1 administrator administrator 5452 Oct 4 17:56 utils.c
-rw-r--r-- 1 administrator administrator 1017 Oct 4 17:56 utils.h
the simplest command to run is:
cat filelist.txt | du -cb | tail -1 | cut -f -1
with following output (in bytes)
69370
Keep in mind that du prints actual disk usage rounded up to a multiple of (usually) 4kb instead of logical file size.
For small files this approximation may not be acceptable.
To sum one directory, you will have to do a while, and export the result to the parent shell.
I used an echo an the subsequent eval :
eval ' let sum=0$(
ls -l | tail -n +2 |\
while read perms link user uid size date day hour name ; do
echo -n "+$size" ;
done
)'
It produces a line, directly evaluated, which looks like
let sum=0+205+1201+1201+1530+128+99
You just have to reproduce twice this command on both folders.
The du command doesn't have a -b option on the unix systems I have available. And there are other ways to get file size.
Assuming you like the idea of a while loop in bash, the following might work:
#!/bin/bash
case "$(uname -s)" in
Linux) stat_opt=(-c '%s') ;;
*BSD|Darwin) stat_opt=(-f '%z') ;;
*) printf 'ERROR: I don'\''t know how to run on %s\n' "$(uname -s)" ;;
esac
declare -i total=0
declare -i count=0
declare filename
while read filename; do
[[ -f "$filename" ]] || continue
(( total+=$(stat "${stat_opt[#]}" "$filename") ))
(( count++ ))
done
printf 'Total: %d bytes in %d files.\n' "$total" "$count"
This would take your list of files as stdin. You can run it in BSD unix or in Linux -- the options to the stat command (which is not internal to bash) are the bit that are platform specific.

Linux - multiple command execution using semicolon

I have a scenario where I need to execute date command and ls -lrth|wc -l command at the same time.
I read somewhere on google that I can do it in the way shown below using the semicolon
ls -lrth | wc -l | ; date
This works super fine!
But the problem is when I want to extract the output of this. This gives a two line output with the output of ls -lrth |wc -l in the first line and the second line has the date output like shown below
$ cat test.txt
39
Mon Oct 26 16:11:20 IST 2015
But it seems like linux is treating these two lines as if its on the same line.
I want this to be formatted to something like this
39,Mon Oct 26 16:11:20 IST 2015
For doing this I am not able to separately access these two lines (not even with tail or head).
Thanks in advance.
EDIT
Why I think linux is treating this as a same line because when I do this as shown below,
$ ls -lrth| wc -l;date | head -1
39
Mon Oct 26 16:24:07 IST 2015
The above reason is for my assumption of the one line thing.
Have you already tried using an echo?
echo $(ls | wc -l) , $(date)
(or something similar, I don't have a Linux emulator here)
If you want in your script
./script.sh
#!/bin/bash
a=$(ls -lrth | wc -l)
b=$(date)
out="$a,$b"
echo "$out"
EDIT
ls -lrth| wc -l;date | head -1
The semicolon simply separates two different commands ";"
Pipe to xargs echo -n (-n means no newline at end):
ls -lrth | wc -l | xargs echo -n ; echo -n ","; date
Testing:
$ ls -lrth | wc -l | xargs echo -n ; echo -n ","; date
11,Mon Oct 26 12:57:14 EET 2015

How do I find the latest date folder in a directory and then construct the command in a shell script?

I have a directory in which I will have some folders with date format (YYYYMMDD) as shown below -
david#machineX:/database/batch/snapshot$ ls -lt
drwxr-xr-x 2 app kyte 86016 Oct 25 05:19 20141023
drwxr-xr-x 2 app kyte 73728 Oct 18 00:21 20141016
drwxr-xr-x 2 app kyte 73728 Oct 9 22:23 20141009
drwxr-xr-x 2 app kyte 81920 Oct 4 03:11 20141002
Now I need to extract latest date folder from the /database/batch/snapshot directory and then construct the command in my shell script like this -
./file_checker --directory /database/batch/snapshot/20141023/ --regex ".*.data" > shardfile_20141023.log
Below is my shell script -
#!/bin/bash
./file_checker --directory /database/batch/snapshot/20141023/ --regex ".*.data" > shardfile_20141023.log
# now I need to grep shardfile_20141023.log after above command is executed
How do I find the latest date folder and construct above command in a shell script?
Look, this is one of approaches, just grep only folders that have 8 digits:
ls -t1 | grep -P -e "\d{8}" | head -1
Or
ls -t1 | grep -E -e "[0-9]{8}" | head -1
You could try the following in your script:
pushd /database/batch/snapshot
LATESTDATE=`ls -d * | sort -n | tail -1`
popd
./file_checker --directory /database/batch/snapshot/${LATESTDATE}/ --regex ".*.data" > shardfile_${LATESTDATE}.log
See BashFAQ#099 aka "How can I get the newest (or oldest) file from a directory?".
That being said, if you don't care for actual modification time and just want to find the most recent directory based on name you can use an array and globbing (note: the sort order with globbing is subject to LC_COLLATE):
$ find
.
./20141002
./20141009
./20141016
./20141023
$ foo=( * )
$ echo "${foo[${#foo[#]}-1]}"
20141023

rsync prints "skipping non-regular file" for what appears to be a regular directory

I back up my files using rsync. Right after a sync, I ran it expecting to see nothing, but instead it looked like it was skipping directories. I've (obviously) changed names, but I believe I've still captured all the information I could. What's happening here?
$ ls -l /source/backup/myfiles
drwxr-xr-x 2 me me 4096 2010-10-03 14:00 foo
drwxr-xr-x 2 me me 4096 2011-08-03 23:49 bar
drwxr-xr-x 2 me me 4096 2011-08-18 18:58 baz
$ ls -l /destination/backup/myfiles
drwxr-xr-x 2 me me 4096 2010-10-03 14:00 foo
drwxr-xr-x 2 me me 4096 2011-08-03 23:49 bar
drwxr-xr-x 2 me me 4096 2011-08-18 18:58 baz
$ file /source/backup/myfiles/foo
/source/backup/myfiles/foo/: directory
Then I sync (expecting no changes):
$ rsync -rtvp /source/backup /destination
sending incremental file list
backup/myfiles
skipping non-regular file "backup/myfiles/foo"
skipping non-regular file "backup/myfiles/bar"
And here's the weird part:
$ echo 'hi' > /source/backup/myfiles/foo/test
$ rsync -rtvp /source/backup /destination
sending incremental file list
backup/myfiles
backup/myfiles/foo
backup/myfiles/foo/test
skipping non-regular file "backup/myfiles/foo"
skipping non-regular file "backup/myfiles/bar"
So it worked:
$ ls -l /source/backup/myfiles/foo
-rw-r--r-- 1 me me 3126091 2010-06-15 22:22 IMGP1856.JPG
-rw-r--r-- 1 me me 3473038 2010-06-15 22:30 P1010615.JPG
-rw-r--r-- 1 me me 3 2011-08-24 13:53 test
$ ls -l /destination/backup/myfiles/foo
-rw-r--r-- 1 me me 3126091 2010-06-15 22:22 IMGP1856.JPG
-rw-r--r-- 1 me me 3473038 2010-06-15 22:30 P1010615.JPG
-rw-r--r-- 1 me me 3 2011-08-24 13:53 test
but still:
$ rsync -rtvp /source/backup /destination
sending incremental file list
backup/myfiles
skipping non-regular file "backup/myfiles/foo"
skipping non-regular file "backup/myfiles/bar"
Other notes:
My actual directories "foo" and "bar" do have spaces, but no other strange characters. Other directories have spaces and have no problem. I 'stat'-ed and saw no differences between the directories that don't rsync and the ones that do.
If you need more information, just ask.
Are you absolutely sure those individual files are not symbolic links?
Rsync has a few useful flags such as -l which will "copy symlinks as symlinks". Adding -l to your command:
rsync -rtvpl /source/backup /destination
I believe symlinks are skipped by default because they can be a security risk. Check the man page or --help for more info on this:
rsync --help | grep link
To verify these are symbolic links or pro-actively to find symbolic links you can use file or find:
$ file /path/to/file
/path/to/file: symbolic link to `/path/file`
$ find /path -type l
/path/to/file
Are you absolutely sure that it's not a symbolic link directory?
try a:
file /source/backup/myfiles/foo
to make sure it's a directory
Also, it could very well be a loopback mount
try
mount
and make sure that /source/backup/myfiles/foo is not listed.
You should try the below command, most probably it will work for you:
rsync -ravz /source/backup /destination
You can try the following, it will work
rsync -rtvp /source/backup /destination
I personally always use this syntax in my script and works a treat to backup the entire system (skipping sys/* & proc/* nfs4/*)
sudo rsync --delete --stats --exclude-from $EXCLUDE -rlptgoDv / $TARGET/ | tee -a $LOG
Here is my script run by root's cron daily:
#!/bin/bash
#
NFS="/nfs4"
HOSTNAME=`hostname`
TIMESTAMP=`date "+%Y%m%d_%H%M%S"`
EXCLUDE="/home/gcclinux/Backups/root-rsync.excludes"
TARGET="${NFS}/${HOSTNAME}/SYS"
LOGDIR="${NFS}/${HOSTNAME}/SYS-LOG"
CMD=`/usr/bin/stat -f -L -c %T ${NFS}`
## CHECK IF NFS IS MOUNTED...
if [[ ! $CMD == "nfs" ]];then
echo "NFS NOT MOUNTED"
exit 1
fi
## CHECK IF LOG DIRECTORY EXIST
if [ ! -d "$LOGDIR" ]; then
/bin/mkdir -p $LOGDIR
fi
## CREATE LOG HEADER
LOG=$LOGDIR/"rsync_result."$TIMESTAMP".txt"
echo "-------------------------------------------------------" | tee -a $LOG
echo `date` | tee -a $LOG
echo "" | tee -a $LOG
## START RUNNING BACKUP
/usr/bin/rsync --delete --stats --exclude-from $EXCLUDE -rlptgoDv / $TARGET/ | tee -a $LOG
In some cases just copy file to another location (like home) then try again

Resources