I've got a script that uses find. So far I've been fine using -exec to get what I need done, but I'm running into a bit of a roadblock now.
If files are found, I want to execute a command once (so -exec {} \; isn't what I'm looking for), and it's a command that doesn't take any files as parameters, so -exec {} \+ won't work (or at least it doesn't with the version of find on my FreeBSD 10.1 system.
What's the best way to go about doing this?
You need to pipe the filenames to xargs.
I think I missed an important clue here: your command is not interested in the filenames. So you just want to run a command if files were found? In that case, you could try something like:
find -name nosuchfile | if read; then echo "hello"; fi;
The -quit primary can be used in conjunction with -exec, -quit is present in both the BSD and GNU find implementations. The example below is constructed to ensure that find terminates after the first match, regardless of the exit status of the utility.
find path... [expression] \( -exec utility \; -quit -or -quit \)
Related
I am trying to execute a shell script which has the following within it:
find /hana/shared/directory -type d -mtime +2 -exec rm -rf {} \;
This works on other SUSE Linux servers but on one. It keeps returning the following:
find: missing argument to -exec
If, however, I place the same syntax into a terminal and run it manually, it runs without issue.
I can see this is a common issue, but I believe I have tried many of the suggestions to no avail and I'm a bit stuck now.
Very carefully read find(1), proc(5), and the GNU Bash documentation.
You might want to run (this is dangerous; see below):
find / -type d mtime +2 -exec /bin/rm -f '{}' \;
(use at least -ok instead of -exec)
And you probably want to clean just your $HOME.
But you should avoid removing files from /proc/, /sys/, /dev/, /lib/, /usr/, /bin/, and /sbin/. See hier(7) and environ(7).
So, I have this simple script which converts videos in a folder into a format which the R4DS can play.
#!/bin/bash
scr='/home/user/dpgv4/dpgv4.py';mkdir -p 'DPG_DS'
find '../Exports' -name "*1080pnornmain.mp4" -exec python3 "$scr" {} \;
The problem is, some of the videos are invalid and won't play, and I've moved those videos to a different directory inside the Exports folder. What I want to do is check to make sure the files are in a folder called new before running the python script on them, preferably within the find command. The path should look something like this:
../Exports/(anything here)/new/*1080pnornmain.mp4
Please note that (anything here) text does not indicate a single directory, it could be something like foo/bar, foo/b/ar, f/o/o/b/a/r, etc.
You cannot use -name because the search is on the path now. My first solution was:
find ./Exports -path '**/new/*1080pnornmain.mp4' -exec python3 "$scr" {} \;
But, as #dan pointed out in the comments, it is wrong because it uses the globstar wildcard (**) unnecessarily:
This checks if /new/ is somewhere in the preceding path, it doesn't have to be a direct parent.
So, the star is not enough here. Another possibility, using find only, could be this one:
find ./Exports -regex '.*/new/[^\/]*1080pnornmain.mp4' -exec python3 "$scr" {} \;
This regex matches:
any number of nested folders before new with .*/new
any character (except / to leave out further subpaths) + your filename with [^\/]*1080pnornmain.mp4
Performances could degrade given that it uses regular expressions.
Generally, instead of using the -exec option of the find command, you should opt to passing each line of find output to xargs because of the more efficient thread spawning, like:
find ./Exports -regex '.*/new/[^\/]*1080pnornmain.mp4' | xargs -0 -I '{}' python3 "$scr" '{}'
A backup program I used recently made duplicates of whole bunch of files throughout my computer because of some setting that I've since changed.
When the backup program made a copy, it renamed the old one original1.thefilename.extension. I'm trying to automatically delete all of these unnecessary files with a simple shell command.
find -type f -name 'original1*' -exec rm {} \;
However, when I try to run this I get
find: missing argument to `-exec'
I've looked all over the web for a solution. I've found suggestions that I should try exec rm +, -exec rm {} +, -exec rm {} \;, -exec rm + etc. but none of them work. I am using Windows 8.1
I would really appreciate any help!
In Windows command shell, you don't need to escape the semicolon.
find -type f -name 'original1*' -exec rm {} ;
Your version of the command should work in a bash shell (like cygwin).
It's interesting that you get the gnu find to execute, because on my Windows 8.1 machine, I get Microsoft's find.
I have to execute command in bash for all files in a folder with the extension ".prot'
The command is called "bezogener_Spannungsgradient" and it's called like that:
bezogener_Spannungsgradient filename.prot
Thanks!
find . -maxdepth 1 -name \*.prot -exec bezogener_Spannungsgradient {} \;
-maxdepth <depth> keeps find from recursing into subdirectories beyond the given depth.
-name <pattern> limits find to files matching the pattern. The escape is necessary to keep bash from expanding the find option into a list of matching files.
-exec <cmd> {} \; executes <cmd> on each found file (replacing {} with the filename). If the command is capable of processing a list of files, use + instead of \;.
I generally recommend becoming familiar with the lots of other options of find; it's one of the most underestimated tools out there. ;-)
You could do this:
for f in *.prot; do
bezogener_Spannungsgradient "$f"
done
Can someone show me to use xargs properly? Or if not xargs, what unix command should I use?
I basically want to input more than (1) file name for input <localfile>, third input parameter.
For example:
1. use `find` to get list of files
2. use each filename as input to shell script
Usage of shell script:
test.sh <localdir> <localfile> <projectname>
My attempt, but not working:
find /share1/test -name '*.dat' | xargs ./test.sh /staging/data/project/ '{}' projectZ \;
Edit:
After some input from everybody and trying -exec, I am finding that my <localfile> filename input with find is also giving me the full path. /path/filename.dat instead of filename.dat. Is there a way to get the basename from find? I think this will have to be a separate question.
I'd just use find -exec here:
% find /share1/test -name '*.dat' -exec ./test.sh /staging/data/project/ {} projectZ \;
This will invoke ./test.sh with your three arguments once for each .dat file under /share1/test.
xargs would pack up all of these filenames and pass them into one invocation of ./test.sh, which doesn't look like your desired behaviour.
If you want to execute the shell script for each file (as opposed to execute in only once on the whole list of files), you may want to use find -exec:
find /share1/test -name '*.dat' -exec ./test.sh /staging/data/project/ '{}' projectZ \;
Remember:
find -exec is for when you want to run a command on one file, for each file.
xargs instead runs a command only once, using all the files as arguments.
xargs stuffs as many files as it can onto the end of the command line.
Do you want to execute the script on one file at a time or all files? For one at a time, use file's exec, which it looks like you're already using the syntax for, and which xargs doesn't use:
find /share1/test -name '*.dat' -exec ./test.sh /staging/data/project/ '{}' projectZ \;
xargs does not have to combine arguments, it's just the default behavior. this properly uses xargs, to execute the commands, as intended.
find /share1/test -name '*.dat' -print0 | xargs -0 -I'{}' ./test.sh /staging/data/project/ '{}' projectZ
When piping find to xargs, NULL termination is usually preferred, I recommend appending the -print0 option to find. After which you must add -0 to xargs, so it will expect NULL terminated arguments. This ensures proper handling of filenames. It's not POSIX proper, but considered well supported. You can always drop the NULL terminating options, if your commands lack support.
Remeber while find's purpose is finding files, xargs is much more generic. I often use xargs to process non-filename arguments.