0

I have the following structure:

FolderA
    Sub1
    Sub2
    filexx.csv
    filexx.doc
FolderB
    Sub1
    Sub2
    fileyy.csv
    fileyy.doc

I want to write a script that will move the .csv files into the folder sub1 for each parent directory (Folder A, Folder B and so on) giving me the following structure:

FolderA
    Sub1
        filexx.csv
    Sub2
        filexx.doc

FolderB
    Sub1
        fileyy.csv
    Sub2
        fileyy.doc

This is what I have till now but I get the error mv: cannot stat *.csv: No such file or directory

for f in */*/*.csv; do
    mv -v "$f" */*/Sub1;
done

for f in */*/*.doc; do
    mv -v "$f" */*/Sub2;
done

I am new to bash scripting so please forgive me if I have made a very obvious mistake. I know I can do this in Python as well but it will be lengthier which is why I would like a solution using linux commands.

3 Answers 3

3
find . -name "*.csv" -type f -execdir mv '{}' Sub1/ \;

Using find, search for all files with the extension .csv and then when we find them, execute a move command from within the directory containing the files, moving the files to directory Sub1

find . -name "*.doc" -type f -execdir mv '{}' Sub2/ \;

Follow the same principle for files with the extension .doc but this time, move the files to Sub2.

Sign up to request clarification or add additional context in comments.

Comments

2

I believe you are getting this error because no file matched your wildcard. When it happens, the for loop will give $f the value of the wildcard itself. You are basically trying to move the file *.csv which does not exist.

To prevent this behavior, you can add shopt -s nullglob at the top of your script. When using this, if no file is found, your script won't enter the loop.

My advise is, make sure you run your script from the correct location when using wildcards like this. But maybe what you meant to do by writing */*/*.csv is to recursively match all the csv files. If that's what you intended to do, this is not the right way to do it.

To recursively match all csv/doc/etc files using native bash you can add shopt -s globstar to the top of your script and use **/*.csv as wildcard

#!/bin/bash
shopt -s globstar nullglob

for f in **/*.csv; do
    mv "$f" Destination/  # Note that $f is surrounded by "" to handle whitespaces in filenames
done

You could also use the find (1) utility to achieve that. But if you're planning to do more processing on the files than just moving them, a for loop might be cleaner as you won't have to inline everything in the same command.

Side note : "Linux commands" as you say are actually not Linux commands, they are part of the GNU utilities (https://www.gnu.org/gnu/linux-and-gnu.en.html)

1 Comment

yes, I am trying to do it recursively! Thank you very much for the side note.
1

If csv files you want to move are in the top directories (from the point of view of the current directory), but not in the subdirectories of them, then simply:

#!/bin/bash

for dir in */; do
    mv -v "$dir"*.csv "${dir}Sub1/"
    mv -v "$dir"*.doc "${dir}Sub2/"
done

If the files in all subdirectories are wanted to be moved similarly, then:

shopt -s globstar
for file in **/*.csv; do
    mv -v "$file" "${file%/*}/Sub1/"
done
for file in **/*.doc; do
    mv -v "$file" "${file%/*}/Sub2/"
done

Note that, the directories Sub1 and Sub2 are relative to the directory where csv and doc files reside.

1 Comment

@user42 note however this is not recursive ! As Nejat stated, this will only work "If csv files you want to move are in the top directories"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.