4
#!/bin/bash
# script to find and move files

SOURCE=/DRIVE2/folder/
DESTDIR=/DRIVE/new3/

MOVEFILES=(mpg avi m4v mp4 3gp mpeg MOV) 
# this line above is not working, its only reading the first extension

find $SOURCE -type f -name *.$MOVEFILES -print | xargs -i mv -v "{}" $DESTDIR 

Can you guys help me with my bash script? The $MOVEFILES part of my script is not working. It's only reading the first extension I put there which is mpg. The rest is being ignored. What is the proper way to write that line?

Also is it possible to include the directory its from lets say /DRIVE2/folder1/folder2/folder3/folder4/file.ext move to /DRIVE/folder3/folder4/file.ext

1
  • 1
    How did you decide which part of folder1/folder2/... to retain?
    – muru
    Nov 13, 2015 at 19:29

4 Answers 4

6

You should probably use -regex instead of -name - with -name, each extension will need a seperate -name test:

find .. -name '*.mp4' -name '*.avi' ...

Instead:

find -regextype posix-extended ... -regex '.*\.(mp4|avi|...)' ...

Given your array, do (ditching the unnecessary xargs):

find "$SOURCE" -regextype posix-extended  -type f -regex ".*\.($(IFS="|"; echo "${MOVEFILES[*]}"))" -exec mv -t "$DESTDIR" {} +

IFS="|"; echo "${MOVEFILES[*]}" is a way to print every element in the MOVEFILES array separated by |.

$MOVEFILES by itself is just the first element in the MOVEFILES array.

6

You can do this by using only shell (bash):

shopt -s extglob globstar nullglob 
mv -t /destination **/*(*.mpg|*.m4v|*.mpeg|*.mp4|*.avi|*.3gp|*.MOV)
  • At first we need to set a few shell options

  • extglob will enable us to do extended pattern matching for filenames

  • globstar, after enabling this, by using ** we can traverse through subdirectories to search for the files

  • nullglob will result in a null string if no file name is matched by our given patterns rather than the patterns themselves

  • Now, *(*.foo|*.bar) is a extglob pattern which will match zero or more occurrences of either *.foo or *.bar

  • So combinedly, **/*(*.mpg|*.m4v|*.mpeg|*.mp4|*.avi|*.3gp|*.MOV) will get us the files to be moved

  • mv -t /destination **/*(*.mpg|*.m4v|*.mpeg|*.mp4|*.avi|*.3gp|*.MOV) will move the files to the /destination.

Example:

$ tree 
.
├── dest
├── foo
│   ├── bar
│   │   ├── baz.md
│   │   ├── egg.3gp
│   │   └── egg.mp4
│   ├── baz.txt
│   ├── spam.avi
│   └── spam.mp3
├── spamegg.MOV
└── spamegg.txt


$ shopt -s extglob globstar nullglob 

$ mv -t dest/ **/*(*.mp3|*.mp4|*.avi|*.3gp|*.MOV)

$ tree 
.
├── dest
│   ├── egg.3gp
│   ├── egg.mp4
│   ├── spam.avi
│   ├── spamegg.MOV
│   └── spam.mp3
├── foo
│   ├── bar
│   │   └── baz.md
│   └── baz.txt
└── spamegg.txt
1
  • 2
    Side note (not for you heemayl, for other people reading this): extglob is always disabled when Bash is run non-interactively (script, bash -c etc) but it's always enabled when Bash is run interactively, so you won't need to turn extglob on unless you're running this from a script (or from bash -c).
    – kos
    Nov 14, 2015 at 9:41
3

Using find with -regex

MOVEFILES=".*\.\(mpg\|avi\|m4v\)"
find "$SOURCE" -type f -regex "$MOVEFILES" -exec mv {} "$DESTDIR" \;

You could also use -iregex instead of -regex for case-insensitve matches like AvI and so on.

All the \ looks ugly, I know. -regextype posix-extended, as used in @Murus answer, looks better.

2
  • Yes, sure, but @mure has the better explanation. You should accept the other answer. ;)
    – A.B.
    Nov 13, 2015 at 20:42
  • Cool, didn't know about find's exec flag. Nov 14, 2015 at 17:29
2

find command allows setting multiple conditions with -o flag and you don't really need xargs, you can use -exec. Personally, I'd change your script to

#!/bin/bash
# script to find and move files

SOURCE=/DRIVE2/folder/
DESTDIR=/DRIVE/new3/


find "$SOURCE" -type f \( -name -o "*.mpg" -o -name "*.avi" -o -name "*.m4v" -o -name "*.mp4" -o -name "*.3gp"  -o -name "*.mpeg" -o -name "*.MOV" \) -exec mv -t "$DESTDIR" {} +

Another approach would be with a for loop , where we work on each extension individually

for EXT in ${MOVEFILES[@]}; do find "$SOURCE" -type f -name "*.$EXT" -exec mv -t "$DESTDIR" {} +   ; done
1
  • -t $DESTDIR is great!
    – A.B.
    Nov 13, 2015 at 19:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .