Bash if file sizes are greater than 1kb - bash

I have created a vi file and I want to check the files in my home directory to see their size. If the size of the regular file is greater than 1kb I want to back it up as a compressed file with .bak extension. I have started with the command du -h --max-depth=0 * | sort -r which list the files like...
10K archive.tar
1.0K activity48
1.0K activity47
1.0K activity46
1.0K activity45
1.0k activity44
1.0K activity43
1.0K activity42
1.0K activity41
1.0K activity40
1.0K activity39
1.0K activity38
These are some of the files listed but my thought is I need to cut field 1 and somehow create an if statement and compare the field something like if [ $x -ge 1.0 ] ; do something. Any thoughts on how I should go about the problem...?

I'd use find:
find . -maxdepth 1 -type f -size +1k -exec gzip -k -S .bak '{}' \;
I'd probably not use a custom extension for the compressed file, though; that's just asking for future confusion.
find searches a directory (. in this case) for files that pass a filter. Complex filters can be constructed; in this relatively simple case, several primitive filters are chained to select
Files that are no more than one level deep into . (i.e., subdirectories are not searched),
are regular files,
1KB or larger, and
for which gzip -k S .bak filename exits with a status code of 0.
The -exec filter is special in that it is considered an action (other actions include -delete and -print). If a filter does not contain an action, an implicit -print action is appended to the filter so that the names of all files that fit the filter are printed. Since our filter contains an action, that does not happen.
In any case, we're not really interested in the result of the -exec filter in this case, just in its side effect of running the specified command. It is useful to know that -exec is also a filter, however, in case you want to chain commands. For example, if you wanted to copy the backup files to another folder after packing them, you could write
find . -maxdepth 1 -type f -size +1k -exec gzip -k -S .bak '{}' \; -exec cp '{}.bak' /some/where/else/ \;
Then cp filename.bak /some/where/else/ would be executed only if gzip -k -S .bak filename returned with an exit status of 0 (that is, if it indicated successful completion).

find . -maxdepth 1 -type f -size +1k -exec gzip {} \;
That ought to do it. Well it produces compressed .gz files.

Related

Check from files in directory which is the most recent in Bash Shell Script

I am making a bash script to run in a directory with files generated everyday and copy the most recent file to another directory.
I am using this now
for [FILE in directory]
do
if [ls -Art | tail -n 1]
something...
else
something...
fi
done
I know this is not alright. I would like to compare the date modified of the files with the current date and if it was equal, copy that file then.
How would that work or is there an easier method to do it?
We could use find:
find . -maxdepth 1 -daystart -type f -mtime -1 -exec cp -f {} dest \;
Explanation:
-maxdepth 1 limits the search to the current directory.
-daystart sets the reference time of -mtime to the beginning of today.
-type f limits the search to files.
-mtime -1 limits the search to files that have been modified less than 1 day from reference time.
-exec cp -f {} dest \; copies the found files to directory dest.
Note that -daystart -mtime -1 means anytime after today 00:00 (included), but also tomorrow or any time in the future. So if you have files with last modification time in year 2042 they will be copied too. Use -mtime 0 if you prefer coping files that have been modified between today at 00:00 (excluded) and tomorrow at 00:00 (included).
Note also that all this could be impacted by irregularities like daylight saving time or leap seconds (not tested).
The newest file is different from file(s) modified today.
Using ls is actually a pretty simple and portable approach. The stdout output format is defined by POSIX (if not printing to a terminal), and ls -A is also in newer POSIX standards.
It should look more like this though:
newest=$(ls -At | head -n 1)
You could add -1, but it AFAIK it shouldn’t be required, as it’s not printing to a terminal.
If you don’t want to use ls, you can use this on linux:
find . -mindepth 1 -maxdepth 1 -type f -exec stat -c ‘%Y:%n’ {} + |
sort -n |
tail -n 1 |
cut -d : -f 2-
Note using 2- not 2 with cut, in case a filename contains :.
Also, the resulting file name will be a relative path (./file), or an empty string if no files exist.

Bash script to move folders based on filesize changes?

I have some automated downloads in a proprietary linux distro.
They go to a temp scratch disk. I want to move them when they're finished to the main RAID array. The best way I can see to do this is to check the folders on the disk to see if the contents have changed in the last minute. If not then its probably finished downloading and then move it.
Assuming there could be hundreds of folders or just one in this location and its all going to the same place. Whats the best way to write this?
I can get a list of folder sizes with
du -h directory/name
The folders can contain multiple files anywhere from 1.5mb to 10GB
Temp Loc: /volume2/4TBScratch/Processing
Dest Loc when complete: /volume1/S/00 Landing
EDIT:
Using this:
find /volume2/4TBScratch/Processing -mindepth 1 -type d -not -mmin +10 -exec mv "{}" "/volume1/S/00 Landing" \;
find: `/volume2/4TBScratch/Processing/test': No such file or directory
4.3#
yet it DOES copy the relevant folders and all files. But the error worries me that something might go wrong in the future.... is it because there is multiple files and it's running the same move command for EACH file or folder in the root folder? But since it moves it all on the first iteration it cant find it on the next ones?
EDIT2:
Using Rsync
4.3# find /volume2/4TBScratch/Processing -mindepth 1 -type d -not -mmin +10 -exec rsync --remove-source-files "{}" "/volume1/S/00 Landing" \;
skipping directory newtest
skipping directory erw
RESOLVED: EDIT3
Resolved with the help in the comments below. Final script looks like this:
find /volume2/4TBScratch/Processing -mindepth 1 -type d -not -mmin +10 -exec rsync -a --remove-source-files "{}" "/volume1/S/00 Landing" \;
find /volume2/4TBScratch/Processing -depth -type d -empty -delete
rsync to move folders and files but leaves empty root dir
the next command finds empty folders and removes them.
Thanks all!
You can use GNU find with options -size for detecting files/folders of certain size and use mv with the -exec option to move to destination directory. The syntax is
find /volume2/4TBScratch/Processing -type d -maxdepth 1 -size -10G -exec mv "{}" "/volume1/S/00 Landing" \;
Using rsync
find /volume2/4TBScratch/Processing -type d -maxdepth 1 -size -10G -exec rsync --remove-source-files "{}" "/volume1/S/00 Landing" \;
The size with a - sign to indicate less than the mentioned size which in this case is 10GB. A note on each of the flags used
-type d -> For identifying only the folders from the source path.
-maxdepth 1 -> To look only on the current source directory and not
being recursive.
-exec -> Execute command following it.
Alternatively, if you want to find files that are last modified over a certain time(minutes), find has an option for -mmin which can be set to a value. E.g. -mmin -5 would return files modified five minutes ago.
So suggest adding it to your requirement, for x as you need and see if the directories are listed, then you can add the -exec option for moving the directories
find /volume2/4TBScratch/Processing -type d -maxdepth 1 -mmin -2 -size -10G
Refer to the GNU documentation for finding files according to size on how this works.
Note:- The double quotes("") are added to avoid Bash from splitting the names containing spaces.

Move files of specific size in Ubuntu using Terminal

I want to move all the files in a specific folder having size of 0 bytes. I know that the following prints all the files with size zero bytes.
find /home/Desktop/ -size 0
But i want to move them to another folder, so i tried :
find /home/Desktop/ -size 0 | xargs -0 mv /home/Desktop/a
But that doesn't work. ? Is there any other way to do it.? What am i doing wrong?
You can do that in find itself using -exec option:
find /home/Desktop/ -size 0 -exec mv '{}' /home/Desktop/a \;
find default prints the file name on the standard output followed by a newline. The option -print0 prints the file name followed by a null character instead. The option -0 of xargs means that the input is terminated by a null character.
find /home/Desktop/ -size 0 -print0 | xargs -0 -I {} mv {} /home/Desktop/a
You could instead use find's option -exec
In both cases consider also using find's option -type f if you only want to find files and the option -maxdepth 1 if you do not want find to descend directories. This is specially usefull in your example since you move the found files to a subdirectory!

Move only files recursively from multiple directories into one directory with mv

I currently have ~40k RAW images that are in a nested directory structure. (Some folders have as many as 100 subfolders filled with files.) I would like to move them all into one master directory, with no subfolders. How could this be accomplished using mv? I know the -r switch will copy recursively, but this copies folders as well, and I do not wish to have subdirectories in the master folder.
If your photos are in /path/to/photos/ and its subdirectories, and you want to move then in /path/to/master/, and you want to select them by extension .jpg, .JPG, .png, .PNG, etc.:
find /path/to/photos \( -iname '*.jpg' -o -iname '*.png' \) -type f -exec mv -nv -t '/path/to/master' -- {} +
If you don't want to filter by extension, and just move everything (i.e., all the files):
find /path/to/photos -type f -exec mv -nv -t '/path/to/master' -- {} +
The -n option so as to not overwrite existing files (optional if you don't care) and -v option so that mv shows what it's doing (very optional).
The -t option to mv is to specify the target directory, so that we can stack all the files to be moved at the end of the command (see the + delimiter of -exec). If your mv doesn't support -t:
find /path/to/photos \( -iname '*.jpg' -o -iname '*.png' \) -type f -exec mv -nv -- {} '/path/to/master' \;
but this will be less efficient, as one instance of mv will be created for each file.
Btw, this moves the files, it doesn't copy them.
Remarks.
The directory /path/to/master must already exist (it will not be created by this command).
Make sure the directory /path/to/master is not in /path/to/photos. It would make the thing awkward!
Make use of -execdir option of find:
find /path/of/images -type f -execdir mv '{}' /master-dir \;
As per man find:
-execdir utility [argument ...] ;
The -execdir primary is identical to the -exec primary with the exception that
utility will be executed from the directory that holds the current
file. The filename substituted for the string ``{}'' is not qualified.
Since -execdir makes find execute given command from each directory therefore only base filename is moved without any parent path of the file.
find <base location of files> -type -f -name \*\.raw -exec mv {} master \;
If your hierachy is only one level deep, here is another way using the automated tools of StringSolver:
mv -a firstfolder/firstfile.raw firstfile.raw
The -a options immediately applies the similar transformation to all similar files at a nesting level 1 (i.e. for all other subfolders).
If you do not trust the system, you can use other options such as -e to explain the transformation or -t to test it on all files.
DISCLAIMER: I am a co-author of this work for academic purposes, and working on a bash script renderer. But the system is already available for testing purposes.

Moving large number of files [duplicate]

This question already has answers here:
Argument list too long error for rm, cp, mv commands
(31 answers)
Closed 3 years ago.
If I run the command mv folder2/*.* folder, I get "argument list too long" error.
I find some example of ls and rm, dealing with this error, using find folder2 -name "*.*". But I have trouble applying them to mv.
find folder2 -name '*.*' -exec mv {} folder \;
-exec runs any command, {} inserts the filename found, \; marks the end of the exec command.
The other find answers work, but are horribly slow for a large number of files, since they execute one command for each file. A much more efficient approach is either to use + at the end of find, or use xargs:
# Using find ... -exec +
find folder2 -name '*.*' -exec mv --target-directory=folder '{}' +
# Using xargs
find folder2 -name '*.*' | xargs mv --target-directory=folder
find folder2 -name '*.*' -exec mv \{\} /dest/directory/ \;
First, thanks to Karl's answer. I have only minor correction to this.
My scenario:
Millions of folders inside /source/directory, containing subfolders and files inside. Goal is to copy it keeping the same directory structure.
To do that I use such command:
find /source/directory -mindepth 1 -maxdepth 1 -name '*' -exec mv {} /target/directory \;
Here:
-mindepth 1 : makes sure you don't move root folder
-maxdepth 1 : makes sure you search only for first level children. So all it's content is going to be moved too, but you don't need to search for it.
Commands suggested in answers above made result directory structure flat - and it was not what I looked for, so decided to share my approach.
This one-liner command should work for you.
Yes, it is quite slow, but works even with millions of files.
for i in /folder1/*; do mv "$i" /folder2; done
It will move all the files from folder /folder1 to /folder2.
find doesn't work with really long lists of files, it will give you the same error "Argument list too long". Using a combination of ls, grep and xargs worked for me:
$ ls|grep RadF|xargs mv -t ../fd/
It did the trick moving about 50,000 files where mv and find alone failed.

Resources