How to move all files with fewer than 5 bytes in BASH? - bash

I have a folder containing many audio files, but some are ~4 bytes and seem to contain nothing, they are 0 seconds long and have no sound. I want to move them to a folder called "temp/".
How can I move all of the files in the folder that have fewer than 5 bytes to this folder?

Find!
You can use find to do this for you:
find . -type f -maxdepth 1 -size -5c -exec mv {} temp/ \;
Explanation
-size -5c grabs all the files less than 5 bytes. The - indicates less than and the c indicates bytes.
-maxdepth 1 prevents you from trying to move the files on top of themselves when it tries to recurse into temp/ (after moving your initial files).
-exec mv {} temp/ \; simply runs mv on each file to put them in temp (the {} is substituted for the name of the file). The escaped semicolon marks the end of the mv command for exec.
There are other sizes available as well:
`b' for 512-byte blocks (this is the default if no suffix is
used)
`c' for bytes
`w' for two-byte words
`k' for Kilobytes (units of 1024 bytes)
`M' for Megabytes (units of 1048576 bytes)
`G' for Gigabytes (units of 1073741824 bytes)

find -size 1c will give you all files that are exactly one byte.
As #user1666959 mentions, you can also use find . -type f -size -4c, which will find all files in the current directory (and subdirectories), that are 4 bytes and smaller.
$ find . -maxdepth 1 -type f -size -4c -exec mv {} temp/ \;
(Yes, you will need the trailing \;.
Note that find -size allows for other exact file size matches (such as 1k), but also allows you to search for files that take up the designated number of blocks on the disk (leaving off the unit).
$ man find
Provides a heap more info about how to use it to search.

find . -maxdepth 1 -type f -size -5c -exec mv '{}' temp/ \;

One solution would be:
find . -type f -maxdepth 1 | xargs du | sort -n |grep "^[0-5]\t"|sed "s/[0-5]//"|sed "s/^.//"|xargs -I ARG mv ARG temp/
It finds all files, lists their sizes, sorts by that, takes all that are size 0,1,2,3,4,5, gets just the filenames, and then runs the mv command on them!

Related

Bash: find files within file size range and report their names to a file

I would like to write a Bash script that selects files within a given folder with file size between 10kB and 100kB. This list of files should be written to a new file. Something like:
fileSelector ~/my-folder-containing-files ~/my-report-file
Can you help me develop such a script using bash?
If you just want to get a list of files within a specific size range, you can try:
$ find . -type f -size +10k -size -101k -exec ls {} \;
notice that since the size is calculated to be rounded up to the next unit, you would actually get a size range between 10241 to 102400 bytes from the above expression
if you want to make the limits to be byte-precise, you can use:
$ find . -type f -size +10239c -size -102401c -exec ls {} \;

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.

Bash if file sizes are greater than 1kb

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.

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!

Delete files with a length of x or more characters

I'm reviewing for an exam and one of the questions is asking me to write a single command that will delete the files in a given directory that are at least 6 characters long.
Example:
person#ubuntumachine:~$ ls
abc.txt, abcdef.txt, 123456.txt, helloworld.txt, rawr.txt
The command would delete the files "abcdef.txt", "12346.txt" and "helloworld.txt".
I'm aware the at the * would be used at some point but I'm not sure what to use to indicate 6 characters long...
Thank you <3
Since the question can have 2 interpretations, both answers are given:
1. To delete files with 6 or more characters in the FILE NAME:
rm ??????*
Explanation:
??????: The ?'s are to be entered literally. Each ? matches any single character. So here it means "match any 6 characters"
*: The * wildcard matches zero or more characters
Therefore it removes any file with 6 or more characters.
Alternatively:
find -type f -name "??????*" -delete
Explanation:
find: invoke the find command
-type f: find only files.
-name "??????*": match any file with at least 6 characters, same idea as above.
-delete: delete any such files found.
2. To delete files with 6 or more characters in its CONTENTS:
find -type f -size +5c -delete
Explanation:
find: invoke the find command
-type f: find only files (not directories etc)
-size +5c: find only files greater than 5 characters long. Note: recall that EOF (end of file) counts as a character in this case. If you'd like to exclude EOF from your counter, change it from 5 to 6.
-delete: delete any such files found
Something like this should work:
$ ls|while read filename; do test ${#filename} -gt 6 && echo rm "$filename"; done
The trick is to use the ${#foo} construct to get the length of the filename.
Once you're satisfied with the output, immediately run the following after the previous command:
$ !! | sh
This repeats the last command (which shows the rm command to delete the files) and pipe it to sh to really execute it.
This will perform the requested logic on the current directory and all subdirectories.
find . -type f -regextype posix-egrep -regex ".*/[^/]{5}[^/]+$" -exec rm -vf {} \;
find .
searches the local directory (change the .
to search elsewhere)
-type f
considers files only
-regextype posix-egrep
use egrep regex syntax (this is what I know)
-regex ".*/[^/]{5}[^/]+$"
find will match all paths matching this regex
the regex deconstructs as follows:
.*/ effectively ignores the path until the filename
[^/]{5} finds 5 characters that are not slashes
[^/]+$ requires at least one more character (thus: 6 or more) that is not a slash to appear before the end of line ($)
-exec rm -vf {} \;
find will replace the {} with each file its search query matches (in this case, files with paths that match our regex). Thus, this achieves the deletion. -vf added to print the results so you know what's happened.
-exec is picky about syntax - the \; is necessary to avoid find: missing argument to '-exec' encountered if a simple ; is used in its place.
You can test this by using -print instead of -exec rm -vf {} \; or simply removing the -exec rm -vf {} \; (-print is find's default behavior)

Resources