Find not working in script, working in terminal prompt - linux

I'm trying to run a bash script in linux (ubuntu but also fedora) but it the find command won't work.
search=\"*${exten[iterext]}\"
find $direc{iterdir} $r_option -iname $search exec -rm {} \\\;
Now to explain the variables:
Exten is array with file extensions read from a text file (no problem here)
direc is also an array of directories read from the command line.
Iterdir and iterext are cicle integer variables.
Now I have two problems:
1- This find command will not delete or display for that matter if I run it inside a script; however if I put an echo before the find and copy paste the output to a command prompt find works fine. I've tried the script under ubuntu and fedora so I assume it's not a bash configuration issue. I should note that the issue seems to the $search as I replaced $search with a hardcoded string (like "*txt) and it works inside the script so it's seems to be a quotation issue.
2 - I run that entire find command and also get find:missing argument to '-exec'
Please help :-( it's driving me insane.

Start simple by placing everything in the find command then worry about parameterizing it.
${exten[iterext]} should be ${exten[$iterext]}
$direc{iterdir} should be ${direc[$iterdir]}
exec -rm should be -exec rm
\\\; should be \;
Quote your variables to prevent word splitting
The following will perform a dry run thanks to the echo. Simply remove the echo when you are satisfied with the output to perform the deletions.
find "${direc[$iterdir]}" "$r_option" -name "*${exten[$iterext]}" -exec echo rm {} \;

Your use of quotes seems a little odd to me. Try this:
find "$direc{iterdir}" $r_option -iname "*${exten[iterext]}" -exec -rm "{}" ";"
Oh, and run your shell script with the -x option. This will print every command line before it is executed.
set -x
find "$direc{iterdir}" $r_option -iname "*${exten[iterext]}" -exec -rm "{}" ";"
set +x

Related

find all files with certain extensions then execute

Why does running this command give me error message: No such file or directory ?
for i in `find ~/desktop -name '*.py'` ; do ./$i ; done
The complete error message makes it much more clear what the problem is:
bash: .//home/youruser/desktop/foo.py: No such file or directory
You can see that there is indeed no such file:
$ .//home/youruser/desktop/foo.py
bash: .//home/youruser/desktop/foo.py: No such file or directory
$ ls -l .//home/youruser/desktop/foo.py
ls: cannot access './/home/youruser/desktop/foo.py': No such file or directory
Here's instead how you can run a file /home/youruser/desktop/foo.py:
$ /home/youruser/desktop/foo.py
Hello World
So to run it in your loop, you can do:
for i in `find ~/desktop -name '*.py'` ; do $i ; done
Here's a better way of doing the same thing:
find ~/desktop -name '*.py' -exec {} \;
or with a shell loop:
find ~/desktop -name '*.py' -print0 | while IFS= read -d '' -r file; do "$file"; done
For an explanation of what ./ is and does, and why it makes no sense here, see this question
Try find and exec option. http://man7.org/linux/man-pages/man1/find.1.html
-exec command ;
Execute command; true if 0 status is returned. All following
arguments to find are taken to be arguments to the command
until an argument consisting of `;' is encountered. The
string `{}' is replaced by the current file name being
processed everywhere it occurs in the arguments to the
command, not just in arguments where it is alone, as in some
versions of find. Both of these constructions might need to
be escaped (with a `\') or quoted to protect them from
expansion by the shell. See the EXAMPLES section for examples
of the use of the -exec option. The specified command is run
once for each matched file. The command is executed in the
starting directory. There are unavoidable security problems
surrounding use of the -exec action; you should use the
-execdir option instead.
-exec command {} +
This variant of the -exec action runs the specified command on
the selected files, but the command line is built by appending
each selected file name at the end; the total number of
invocations of the command will be much less than the number
of matched files. The command line is built in much the same
way that xargs builds its command lines. Only one instance of
`{}' is allowed within the command, and (when find is being
invoked from a shell) it should be quoted (for example, '{}')
to protect it from interpretation by shells. The command is
executed in the starting directory. If any invocation with
the `+' form returns a non-zero value as exit status, then
find returns a non-zero exit status. If find encounters an
error, this can sometimes cause an immediate exit, so some
pending commands may not be run at all. This variant of -exec
always returns true.
The paths returned by the find statement will be absolute paths, like ~/desktop/program.py. If you put ./ in front of them, you get paths like ./~/desktop/ which don’t exist.
Replace ./$i with "$i" (the quotes to take care of file names with spaces etc.).
You should use $i and not ./$i
I was doing the same thing this exact moment. I wanted a script to find if there's any flac files in the directory and convert it to opus.
Here is my solution:
if test -n "$(find ./ -maxdepth 1 -name '*.flac' -print -quit)"
then
do this
else
do nothing
fi

Embedding a bash command inside the mv command

I have a directory that contains a list of files having the following format:
240-timestamp1.ts
240-timestamp2.ts
...
360-timestamp1.ts
360-timestamp2.ts
Now, I want to implement a bash command which matches the files that start with '240' and renames them so that instead of '240-timestampX.ts' the files look like '240-human-readable-timestampX.ts'.
I have tried the following:
find . -maxdepth 1 -mmin +5 -type f -name "240*"
-exec mv $0 {$0/240-***and here I want to insert
either stat -c %y filename or date -d #timestampX***} '{}' \;
I stuck here because I don't know if I can embed a bash command inside the mv command. I know the task may look a bit confusing and over-complicated, but I would like to know if it is possible to do so. Of course I can create a bash script that would go through all the files in the directory and while loop them with changing their respective names, but somehow I think that a single command would be more efficient (even if less readable).
The OS is Linux Ubuntu 12.04.5
The shell is bash
Thank you both Kenavoz and Kurt Stutsman for the proposed solutions. Both your answers perform the task; however, I marked Kenavoz's answer as the accepted one because of the degree of similarity between my question and Kenavoz's answer. Even if it is indeed possible to do it in a cleaner way with omitting the find command, it is necessary in my case to use the respective command because I need to find files older than X units of time. So thank you both once again!
In case you want to keep your mmin option, your can use find and process found files with a bash command using xargs :
find . -maxdepth 1 -mmin +5 -type f -name "240*.ts" | xargs -L 1 bash -c 'mv "${1}" "240-$(stat -c %y ${1}).ts"' \;
In bash if all your files are in a single directory, you don't need to use find at all. You can do a for loop:
for file in 240-*; do
hr_timestamp=$(date -d $(echo "$file" | sed 's/.*-\([0-9]*\)\.ts/\1/'))
mv "$file" "240-$hr_timestamp.ts"
done

Need assistance for creating a simple bash script

I've created this bash file putting on it a secuence of commands i often run for synching files from my digital camera. the point is it doesn't to ANYTHING! What am i missing?
thank you!
code:
#!/bin/bash
#temporal
mkdir /tmp/canon
#copy files from camera
rsync -r /run/user/mango/gvfs/g*/DCIM /tmp/canon
cd /tmp/canon
#get files from subdirs
find ./ -name '*.JPG' -exec mv '{}' ./ \;
#remove dirs
ls -l | awk -F'[0-9][0-9]:[0-9][0-9]' '/^d/{print $NF}'| xargs -i rm -rf '{}' \;
#recreate folder structure with year|month pattern
jhead -n%Y/%m/%f *.JPG
#Sync with external HD
rsync -r --ignore-existing . /media/mango/WD/FOTOS/
If it does not even do the mkdir, then it sound most likely that the version of the script you want is not the one running. Try using an qualified path, such as ./myscript or an absolute path, like /home/joe/bin/myscript. The command type myscript will tell from where the shell is running it.
Also, try running the script after adding set -x to the top of the script or using bash -x myscript; that will show every line as it is executed.
If this still doesn't help, there could be bash startup code, such as in .bashrc getting in the way. That's much harder to diagnose, although the same set -x can be used, although with great caution unless a second user can login and edit this user's startup scripts since mistakes in startup scripts can make it impossible to login to the system.
Try this
chmod +x yourscriptname
./yourscriptname
Make usre you are running the same script you made.

Running command recursively in linux

I'm trying to come up with a command that would run mp3gain FOLDER/SUBFOLDER/*.mp3 in each subfolder, but I'm having trouble understanding why this command doesn't work:
find . -type d -exec mp3gain \"{}\"/*.mp3 \;
When run, I get error Can't open "./FOLDER/SUBFOLDER"/*.mp3 for reading for each folder and subfolder.
If I run command manually with mp3gain "./FOLDER/SUBFOLDER"/*.mp3 it works. What's going wrong?
If you have a fixed data structure like
folder1/subfolder1/
folder1/subfolder2/
folder2/subfolder1/
[...]
and using zsh or bash version >=4.0 you could try
mp3gain **/*.mp3
But to make sure check the output of
ls **/*.mp3
before you are getting serious with mp3gain.
When you run mp3gain "./FOLDER/SUBFOLDER"/*.mp3 from your shell, the *.mp3 is getting expanded by the shell before being passed to mp3gain. When find runs it, there is no shell involved, and the *.mp3 is getting passed literally to mp3gain. The latter has no idea how to deal with wildcards (because normally it doesn't have to).
Hmmm. Just tried this to test how the directory is parsed by replacing mp3gain with echo and it works:
find . -type d -exec echo {}\/\*.mp3 \;
Try running your version of the command but with echo to see the file output for yourself:
find . -type d -exec echo \"{}\"/*.mp3 \;
Seems the quotes get in the way in your original command.
this works...
find /music -name *mp3 -exec mp3gain -r -k {} \;

Bash script cd issues

Hi all I have some problems with my script. I've read that changing the current directory from within a script is a bit of an issue. Basically I am looking for a single php file with a project folder and any sub-folders in it. And I want to change the directory to where that folder is and perform a command for it. So far no luck.
function findPHP(){
declare -a FILES
FILES=$(find ./ -name \*.php)
for file in "${FILES[#]}"
do
DIR=`dirname file`
( cd $DIR && doSomethingInThisDir &(...))
done
Any help would be greatly appreciated.
You are trying to iterate over FILES as an array, but it only has one element. In order to make the result of your subshell into an array, you can:
FILES=($(find ./ -name \*.php))
Note that it splits file names on spaces, so even though you properly quote below, it won't help. Alternatively, you could just let it split below (i.e. using your existing FILES) and use instead:
for file in $FILES
If you are using bash 4, you may want to have a look at recursive globbing... this would make it a bit easier:
for file in **/*.php
Note that you have to have the globstar shell option set, which you could enable with shopt -s globstar. This way is simpler and won't break on whitespace.
Also, you probably want $file here:
DIR=`dirname $file`
Or just use parameter expansion:
DIR=${file%/*}
There is no reason to use an array, or store the file list in anyway. If your find supports -execdir (eg gnufind 4.2.27), then use it. Otherwise, cd in a subshell as you have done:
#!/bin/bash
doSomethingInThisDir() ( cd $(dirname $1); ... )
export -f doSomethingInThisDir
find . -type f -exec bash -c 'doSomethingInThisDir {}' \;
I have defined the function using () instead of {}, but that is not necessary in this case. Normally, using () causes the function to run in a subshell, but that happens here anyway because find runs a separate process for each file.

Resources