Create Unix shell script to move non empty files from Source directory to Target directory and add timestamp to them - linux

I am trying to Create a shell script to move non empty files from Source directory to Target directory and add timestamp to them.
I am using
find . -type f -size +0 -print0 | xargs -I {} -r0 mv {} $Tgt_dir/{}_`date +%m%d%Y`
but its not working. Could you please help.
Thanks

You can use -printf in find to print the mv command with the full path of the source and just the basename in the destination, and pipe that to the shell:
date=$(date +%m%d%Y)
find . -type f -size +0 -printf "mv '%p' '$Tgt_dir/%f_$date'" | bash
%p is the full pathname, %f is the basename.
To move files with at least one line, write a command that counts the number of lines:
date=$(date +%m%d%Y)
find "$Src_dir" -type f -size +0 -printf "if [ $(wc -l '$p') -gt 1 ]; then mv '%p' '$Tgt_dir/%f_$date'; fi" | bash

Related

I want to get an output of the find command in shell script

Am trying to write a script that finds the files that are older than 10 hours from the sub-directories that are in the "HS_client_list". And send the Output to a file "find.log".
#!/bin/bash
while IFS= read -r line; do
echo Executing cd /moveit/$line
cd /moveit/$line
#Find files less than 600 minutes old.
find $PWD -type f -iname "*.enc" -mmin +600 -execdir basename '{}' ';' | xargs ls > /home/infa91punv/find.log
done < HS_client_list
However, the script is able to cd to the folders from HS_client_list(this file contents the name of the subdirectories) but, the find command (find $PWD -type f -iname "*.enc" -mmin +600 -execdir basename '{}' ';' | xargs ls > /home/infa91punv/find.log) is not working. The Output file is empty. But when I run find $PWD -type f -iname "*.enc" -mmin +600 -execdir basename '{}' ';' | xargs ls > /home/infa91punv/find.log as a command it works and from the script it doesn't.
You are overwriting the file in each iteration.
You can use xargs to perform find on multiple directories; but you have to use an alternate delimiter to avoid having xargs populate the {} in the -execdir command.
sed 's%^%/moveit/%' HS_client_list |
xargs -I '<>' find '<>' -type f -iname "*.enc" -mmin +600 -execdir basename {} \; > /home/infa91punv/find.log
The xargs ls did not seem to perform any useful functionality, so I took it out. Generally, don't use ls in scripts.
With GNU find, you could avoid the call to an external utility, and use the -printf predicate to print just the part of the path name that you care about.
For added efficiency, you could invoke a shell to collect the arguments:
sed 's%^%/moveit/%' HS_client_list |
xargs sh -c 'find "$#" -type f -iname "*.enc" -mmin +600 -execdir basename {} \;' _ >/home/infa91punv/find.log
This will run as many directories as possible in a single find invocation.
If you want to keep your loop, the solution is to put the redirection after done. I would still factor out the cd, and take care to quote the variable interpolation.
while IFS= read -r line; do
find /moveit/"$line" -type f -iname "*.enc" -mmin +600 -execdir basename '{}' ';'
done < HS_client_list >/home/infa91punv/find.log

Display file size with find command prior to the paths

I'm trying to find a way to combine the results that are displayed with the find command, e.g:
$find /home -name "requirements_*"
/home/cobayo/Obligatorio Test 2/requirements_richard
/home/cobayo/Obligatorio Test 2/requirements_paul
/home/cobayo/Obligatorio Test 2/requirements_george
/home/cobayo/Obligatorio Test 1/Subdirectorio/requirements_joe
So here I get all of the paths of the files I am looking for, what I would like to do is to get the file size listed prior the paths.
For instance:
34K "/home/cobayo/Obligatorio Test 2/requirements_richard"
I have some commands, even a little script does show me file sizes.
For example, this one:
find /home -name "requirements_*" -mtime -1 -print0 | du --files0-from=- -hc |
tail -n1*
And it shows the TOTAL size of all files with the name given as a parameter as results.
But then, I also have this little script:
FILENAME=$1
FILESIZE=$(stat -c%s "$FILENAME")
echo "Size of $FILENAME = $FILESIZE bytes."
Which is perfect as I get the file in K of the specific file. I tried to mix them up using a list:
*for i in `find /home -name "requirements_*"`
do
echo `stat -c%s $i`
done*
But I don't get the results Im looking for, in fact it tells me the files don't exist :(
You could use -exec du -hs {} \;:
$ find -type f -exec du -hs {} \;
4.0K ./proj-2/README.md
4.0K ./proj-2/file.c
4.0K ./proj-2/file.h
or alternatively, if your find supports it:
find -type f -exec du -hs {} +
which calls du just once.
Or -printf with the %k formatting parameter for the size in kB:
$ find -type f -printf '%kK\t%p\n'
4K ./proj-2/README.md
4K ./proj-2/file.c
4K ./proj-2/file.h
Manual references:
-exec, single file
-exec, multiple files
-printf
If you do not put the variable $i between double quotes the spaces will be interpreted as separator and your command will try to asses the following files independently :
/home/cobayo/Obligatorio
Test
1/Subdirectorio/requirements_joe
/home/cobayo/Obligatorio
Test
2/requirements_richard
/home/cobayo/Obligatorio
Test
2/requirements_paul
/home/cobayo/Obligatorio
Test
2/requirements_george
what gives you the error: files don't exist
You can try:
find . -name "requirements_*" -print0 | xargs -0 -I {} bash -c "echo -n 'Size of {} = ' && stat -c%s '{}' | tr -d '\n' && echo ' bytes.'"

Recursively prepend text to file names

I want to prepend text to the name of every file of a certain type - in this case .txt files - located in the current directory or a sub-directory.
I have tried:
find -L . -type f -name "*.txt" -exec mv "{}" "PrependedTextHere{}" \;
The problem with this is dealing with the ./ part of the path that comes with the {} reference.
Any help or alternative approaches appreciated.
You can do something like this
find -L . -type f -name "*.txt" -exec bash -c 'echo "$0" "${0%/*}/PrependedTextHere${0##*/}"' {} \;
Where
bash -c '...' executes the command
$0 is the first argument passed in, in this case {} -- the full filename
${0%/*} removes everything including and after the last / in the filename
${0##*/} removes everything before and including the last / in the filename
Replace the echo with a mv once you're satisfied it's working.
Are you just trying to move the files to a new file name that has Prepend before it?
for F in *.txt; do mv "$F" Prepend"$F"; done
Or do you want it to handle subdirectories and prepend between the directory and file name:
dir1/PrependA.txt
dir2/PrependB.txt
Here's a quick shot at it. Let me know if it helps.
for file in $(find -L . -type f -name "*.txt")
do
parent=$(echo $file | sed "s=\(.*/\).*=\1=")
name=$(echo $file | sed "s=.*/\(.*\)=\1=")
mv "$file" "${parent}PrependedTextHere${name}"
done
This ought to work, as long file names does not have new line character(s). In such case make the find to use -print0 and IFS to have null.
#!/bin/sh
IFS='
'
for I in $(find -L . -name '*.txt' -print); do
echo mv "$I" "${I%/*}/prepend-${I##*/}"
done
p.s. Remove the echo to make the script effective, it's there to avoid accidental breakage for people who randomly copy paste stuff from here to their shell.

Pass a large variable into the diff command via bash

I am writing a script which does a checksum (md5sum) on a forum web directory.
It is a bash script. With the idea being to do a checksum on all the files in the directory, and then compare it to a text file which has a list of checksums.
The script works if I pass it into a text file, and then do a diff command between the text file and my list of known checksums, but I would like to not have it write to a text file and then have to remove the text file at the end of the script, hence why I am using a variable
The script below fails with the error:
/usr/bin/diff: Argument list too long
cd /var/www/html/forum/
VAR1=$(find . -type d \( -name store_sitemap \) -prune -o -type f -exec md5sum {} \; | grep -v "files\|that\|change")
/usr/bin/diff "${VAR1}" "/root/scripts/forum_checkum_original.txt"
How can I pass my variable along so that I can runn the diff command on it?
EDIT: with the help of the user devnull (thank you again) here is the completed and working script:
cd /var/www/html/forum/
MAIL=$(/usr/bin/diff <(find . -type d \( -name store_sitemap \) -prune -o -type f -exec md5sum {} \; | grep -v "files\|that\|change") /root/scripts/forum_checkum_original.txt)
if [[ -n $(/usr/bin/diff <(find . -type d \( -name store_sitemap \) -prune -o -type f -exec md5sum {} \; | grep -v "files that change") /root/scripts/forum_checkum_original.txt) ]]; then
echo "$MAIL" | mail -s "Forum Checksum" yourmailaddress#yourdomain.com
else
echo "no files have been changed"
fi
diff compares files, not variables. Use Process Substitution instead.
An equivalent of what you're trying to do would be:
/usr/bin/diff <(find . -type d \( -name store_sitemap \) -prune -o -type f -exec md5sum {} \; | grep -v "bidorbuy.log") /root/scripts/forum_checkum_original.txt
If you want to keep it in a variable you can give diff the variable as a filedescriptor by doing:
diff <(echo "$MAIL") "/root/scripts/forum_checkum_original.txt"

Bash script to find files in a list, copy them to dest, print files not found

I would like to build on the answer I found here: Bash script to find specific files in a hierarchy of files
find $dir -name $name -exec scp {} $destination \;
I have a file with a list of file names and I need to find those files on a backup disk, then copy those files found to a destination folder, and lastly print the files that could not be found to a new file.
the last step would be helpful so that I wouldn't need to make another list of files copied and then do a compare with original list.
If the script can then make a list of the copied files, and do a compare, then print the differences, then that's exactly what's required. Unless the shell process find can print to file each time it "Can't find" a file.
Assuming that your list is separated by newlines; something like this should work
#!/bin/bash
dir=someWhere
dest=someWhereElse
toCopyList=filesomewhere
notCopied=filesomewhereElse
while read line; do
find "$dir" -name "$line" -exec cp '{}' $dest \; -printf "%f\n"
done < "$toCopyList" > cpList
#sed -i 's#'$dir'/##' cpList
# I used # instead of / in sed to not confuse sed with / in $dir
# Also, I assumed the string in $dir doesnot end with a /
cat cpList "$toCopyList" | sort | uniq -c | sed -nr '/^ +1/s/^ +1 +(.*)/\1/p' > "$notCopied"
# Will not work if you give wild cards in your "toCopyList"
Hope it helps
while read fname ; do
find /FROM/WHERE/TO/COPY/ \
-type f \
-name "$fname" \
-exec cp \{\} /DESTINATION/DIR/ \; 2>/dev/null
find /DESTINATION/DIR/ \
-type f \
-name "$fname" &>/dev/null || \
echo $fname
done < FILESTOCOPY > MISSEDFILES
Will do.

Resources