"dir/*: No such file or directory" with find -exec ... "{}/*" - bash

The current directory contains files and directories. The directories have no sub-directories, but may contain zero or more files, for example:
./file1
./file2
./directory1/file3
./directory2/file4
./directory2/file5
./directory3/
When I execute find . -type d -maxdepth 1 I get a listing of the directories:
./directory1
./directory2
If I execute mv ./directory1/* . all files in directory1 are moved to the current level . so I thought I could use find -exec to do everything in one go:
find . -type d -maxdepth 1 -exec mv "{}/*" . \;
But I get this response:
mv: rename ./directory1/* to ./*: No such file or directory
How can I move all the files in subdirectories to the current level?

Globbing (replacing foo/* with foo/dirA, foo/dirB, etc) is performed by the shell, not by mv. find -exec doesn't start a shell unless you do so manually; for example:
find . -type d -mindepth 1 -maxdepth 1 \
-exec sh -c 'for dir; do mv -- "$dir"/* .; done' _ {} +

There's no real need to use find. You can do it with a single mv to move the files and rmdir to remove the now-empty directories.
mv */* .
rmdir */

Related

Bash - move all files from subdirectories to up folders

/volume1/TVPack/A/Folder1/Folder11/ --> files
/volume1/TVPack/A/Folder2/Folder22/ --> files
/volume1/TVPack/B/Folder3/Folder33/ --> files
(the list goes on)...
I want to move all files from Folder11/22 to /volume1/TVPack/A
and all files from Folder33 to /volume1/TVPack/B
etc
ie, move 2 levels up
The code that I'm using is
find /volume1/TVPack/*/ -type f -mindepth 3 -exec mv -- {} /volume1/TVPack/*/ \;
But this put all the files only in folder B, instead of putting the respective files to A and the respectives files to B.
I'm doing this on a task scheduler, so I think that I can't use mv * .[^.]*
Does anybody know how to do it?
Use -execdir, which cd's to the directory each file is in before executing the command.
find /volume1/TVPack/*/ -type f -mindepth 3 -execdir mv -- {} ../../ \;
Even better, use + instead of \; to minimize the number of mv commands.
find /volume1/TVPack/*/ -type f -mindepth 3 -execdir mv -t ../../ -- {} +

Iterate over folders and move files up one level

I have a directory structure that looks like this:
/images
/1
/.tmp
image1.jpg
image2.jpg
/2
.tmp
image1.jpg
image2.jpg
image3.jpg
/3
.tmp
image1.jpg
image2.jpg
What I need is to move all of those files in .tmp up one level, so their paths are images/1/image1.jpg rather than images/1/.tmp/image1.jpg. The issue is that I have hundreds or thousands of these numbered folders, so doing it by hand would take forever.
Is there an OS X or Unix shell command that I could iterate over each /.tmp folder and move the contents up a level, or something like:
mv images/*/.tmp/* images/< the current dir being iterated over>/*
If your find supports the -execdir command (which OSX's find apparently does), then you could do:
find . -iname '*.jpg' -execdir mv {} .. \;
-execdir runs the command from the directory where the file was found, so .. will refer to that file's directory's parent directory.
You can refine this to force matching a .tmp directory:
find . -path '*/.tmp/*.jpg' -execdir mv {} .. \;
However, * in -path also matches /, so this will also match, for example, images/.tmp/foo/image.jpg.
You could do:
find . -type d -name .tmp -print0 | xargs -0I_ find _ -maxdepth 1 -name '*.jpg' -execdir mv {} .. \;
Or, using find's regex support:
find . -regex '.*/\.tmp/[^/]*\.jpg' -execdir mv {} .. \;
From scratch without testing:
cd /images
for i in $(find ./ -name *.jpg)
do
d = $(dirname $i)
d = $(dirname $d)
echo $i $d
# test! before use the next
# mv $i $d
done

removing path prefix from find results

At the simplest, if I execute
find . -type f -exec cp {} /new/path/{}
The path that is expanded is /new/path/./path/to/file. I would like to remove that ./ that is prefixed by the find command before I use {} in the exec.
I am using the builtin Freebsd find, but I do have access to gnufind if that will help (though I do not normally use gnufind).
Where you will have a problem is when find descends into subdirectories, and it tries to exec something like cp ./foo/bar.txt /new/path/./foo/bar.txt and "/new/path" has no subdirectory "foo" -- you might want to:
specify -maxdepth 1 so you do not descend into subdirs
find . -maxdepth 1 -type f -exec cp {} /new/path/{} \;
just use a directory destination for cp, so the files end up in a single dir (will suffer from collisions if you have "./foo/bar.txt" and "./qux/bar.txt")
find . -type f -exec cp -t /new/path {} +
use tar to copy the whole tree: this will preserve directory structure
tar cf - . | ( cd /new/path && tar xvf - )

A script that iterates over all files in folder

There is a script on a server that I need to run over all the files in a folder. To run this script over one file I use this shell script:
for input in /home/arashsa/duo-bokmaal/Bokmaal/DUO_BM_28042.txt ; do
name=$(basename "$input")
/corpora/bokm/tools/The-Oslo-Bergen-Tagger/./tag-lbk.sh "$input" > "/home/arashsa/duo-bokmaal-obt/$name"
done
I'm terrible at writing shell scripts, and have not managed to found out how to iterate over files. What I want it is to make the script iterate over all files in a given folder that end with .txt and not those that end with _metadata.txt. So I'm thinking I would give it the folder path as argument, make it iterate over all the files in that folder, and run script on files ending with .txt and not _metadata.txt
Use find and the exec option.
$ find /path/to/dir -exec <command here> \;
Each file or directory can be obtained by using {}.
Example usage: $ find . -exec echo {} \;, this will echo each file name recursively or directory name in the current directory. You can use some other options to further specify the desired files and directories you wish to handle. I will briefly explain some of them. Note that the echo is redundant because the output of find will automatically print but I'll leave it there to illustrate the working of exec. This being said, following commands yield the same result: $ find . -exec echo {} \; and $ find .
maxdepth and mindepth
Specifying the maxdepth and mindepth allows you to go as deep down the directory structure as you like. Maxdepth determines how many times find will enter a directory and mindepth determines how many times a directory should be entered before selecting a file or dir.
Example usages:
(1) listing only elements from this dir, including . (= current dir).
(2) listing only elements from current dir excluding .
(3) listing elements from root dir and all dirs in this dir
(1)$ find . -maxdepth 1 -exec echo {} \;
(2)$ find . -mindepth 1 -maxdepth 1 -exec echo {} \;
# or, alternatively
(2)$ find . ! -path . -maxdepth 1 -exec echo {} \;
(3)$ find / -maxdepth 2 -exec echo {} \;
type
Specifying a type option allows you to filter files or directories only, example usage:
(1) list all files in this dir
(2) call shell script funtion func on every directory in the root dir.
(1)$ find . -maxdepth 1 -type f -exec echo {} \;
(2)$ find / -maxdepth 1 -type d -exec func {} \;
name & regex
The name option allows you to search for specific filenames, you can also look for files and dirs using a regex format.
Example usage: find all movies in a certain directory
$ find /path/to/dir -maxdepth 1 -regextype sed -regex ".*\.\(avi\|mp4\|mkv\)"
size
Another filter is the file size, any file or dir greater than this value will be returned. Example usage:
(1) find all empty files in current dir.
(2) find all non empty files in current dir.
(1)$ find . -maxdepth 1 -type f -size 0
(2)$ find . -maxdepth 1 -type f ! -size 0
Further examples
Move all files of this dir to a directory tmp present in .
$ find . -type f -maxdepth 1 -exec mv {} tmp \;
Convert all mkv files to mp4 files in a dir /path/to/dir and child directories
$ find /path/to/dir -maxdepth 2 -regextype sed -regex ".*\.mkv" -exec ffmpeg -i {} -o {}.mp4 \;
Convert all your jpeg files to png (don't do this, it will take very long to both find them and convert them).
$ find ~ -maxdepth 420 -regextype sed -regex '.*\.jpeg' -exec mogrify -format png {} \;
Note
The find command is a strong tool and it can prove to be fruitful to pipe the output to xargs. It's important to note that this method is superior to the following construction:
for file in $(ls)
do
some commands
done,
as the latter will handle files and directories containing spaces the wrong way.
In bash:
shopt -s extglob
for input in /dir/goes/here/*!(_metadata).txt
do
...
done

find command is able to find the directories but find with -exec rm -r is not able to delete

i am running this find command
find . -mindepth 3 -name [1-9]* -type d
and its returning a set of results
like
./B*********/*/output/simulation/9
./B********/*/output/simulation/8
./B********/*/output/simulation/7
./B********/*/output/simulation/5
./B********/*/output/simulation/6
./B********/*/output/simulation/4
now when I am running
find . -mindepth 3 -name [1-9]* -type d -exec rm -r {} \;
on the same directory
find :./B*********/*/output/simulation/9 No such file or directory
find :./B********/*/output/simulation/8 No such file or directory
find :./B********/*/output/simulation/7 No such file or directory
find :./B********/*/output/simulation/5 No such file or directory
find :./B********/*/output/simulation/6 No such file or directory
find :./B********/*/output/simulation/4 No such file or directory
any idea what is going wrong
P.S I have used * to hide the file name for sake of confidentiality
Try this find with quotes around {}:
find . -mindepth 3 -name [1-9]* -type d -exec rm -rf '{}' \;

Resources