Shell stop script if find command fails - shell

Good day.
In a script of fine i have the following find command:
find -maxdepth 1 \! -type d -name "some_file_name_*" -name "*.txt" -name "*_${day_month}_*" -exec cp {} /FILES/directory1/directory2/directory3/ +
I want to know how to stop the script if the command does't find anything.

Use GNU xargs with the -r switch and a pipeline to ensure the output of find is passed to cp only if its non-empty.
find -maxdepth 1 \! -type d -name "some_file_name_*" -name "*.txt" -name "*_${day_month}_*" \
| xargs -r I{} cp "{}" /FILES/directory1/directory2/directory3/
I{} is a place-holder for the output from the find command which is passed to cp,
The flags, -r and I{} represent the following according to the man xargs page,
-r, --no-run-if-empty
If the standard input does not contain any nonblanks, do not run
the command. Normally, the command is run once even if there is
no input. This option is a GNU extension.
-I replace-str
Replace occurrences of replace-str in the initial-arguments with
names read from standard input.

You may add -exec false {} so you get a false exit status when something is found (which makes it a bit upside-down though)
if find . -name foo -exec echo ok ';' -exec false {} +
then
echo 'not found'
exit
fi
echo found
See similar question in stackexchange: How to detect whether “find” found any matches?, in particular this answer which suggests the false trick

Related

Find command output to echo without variable assignment, in one line

I'm trying to write one line of code that finds all .sh files in the current directory and its subdirectories, and print them without the .sh extension (preferably without the path too).
I think I got the find command down. I tried using the output of
find . -type f -iname "*.sh" -print
as input for echo, and formatting it along these lines
echo "${find_output%.sh}"
However, I cannot get it to work in one line, without variable assigment.
I got inspiration from this answer on stackoverflow https://stackoverflow.com/a/18639136/15124805
to use this line:
echo "${$( find . -type f -iname "*.sh" -print)%.sh}"
But I get this error:
ash: ${$( find . -type f -iname "*.sh" -print)%.sh}: bad substitution
I also tried using xargs
find . -type f -iname "*.sh" -print |"${xargs%.sh}" echo
But I get a "command not found error" -probably I didn't use xargs correctly, but I'm not sure how I could improve this or if it's the right way to go.
How can I make this work?
That's the classic useless use of echo. You simply want
find . -type f -iname "*.sh" -exec basename {} .sh \;
If you have GNU find, you can also do this with -printf.
However, basename only matches .sh literally, so if you really expect extensions with different variants of capitalization, you need a different approach.
For the record, the syntax you tried to use for xargs would attempt to use the value of a variable named xargs. The correct syntax would be something like
find . -type f -iname "*.sh" -print |
xargs -n 1 sh -c 'echo "${1%.[Ss][Hh]}"' _
but that's obviously rather convoluted. In some more detail, you need sh because the parameter expansion you are trying to use is a feature of the shell, not of echo (or xargs, or etc).
(You can slightly optimize by using a loop:
find . -type f -iname "*.sh" -print |
xargs sh -c 'for f; do
echo "${f%.[Ss][Hh]}"
done' _
but this is still not robust for all file names; see also https://mywiki.wooledge.org/BashFAQ/020 for probably more than you realized you needed to know about this topic. If you have GNU find and GNU xargs, you can use find ... -print0 | xargs -r0)

find: missing argument to `-exec`

I'm trying to get my Shell script working, but I keep getting: find: missing argument to '-exec'
#!/bin/sh
echo Hello World
find ./public_html/var/session/ -type f -name "sess*" -mtime +30 -exec rm -f {} \;
echo DONE
I've been trying to find help in these solutions but sadly none of them have solved my problem.Shell script - find: missing argument to `-exec' find: missing argument to -exec
Another option not involving -exec is to pipe the result to xargs and then rm on it:
find ./public_html/var/session/ -type f -name "sess*" -mtime +30 | xargs rm -f
man xargs
Note:
xargs supports executing in parallel with -P command, see man page.

How do I use find command with pipe in bash?

The directory structure looks like
home
--dir1_foo
----subdirectory.....
--dir2_foo
--dir3_foo
--dir4_bar
--dir5_bar
I'm trying to use 'find' command to get directories containing specific strings first, (in this case 'foo'), then use 'find' command again to retrieve some directories matching conditions.
So, I first tried
#!/bin/bash
for dir in `find ./ -type d -name "*foo*" `;
do
for subdir in `find $dir -mindepth 2 -type d `;
do
[Do some jobs]
done
done
, and this script works fine.
Then I thought that using only one loop with pipe like below would also work, but this does not work
#!/bin/bash
for dir in `find ./ -type d -name "*foo*" | find -mindepth 2 -type d `;
do
[Do some jobs]
done
and actually this script works the same as
for dir in `find -mindepth 2 -type d`;
do
[Do some jobs]
done
, which means that the first find command is ignored..
What is the problem?
What your script is doing is not a good practice and has lot of potential pitfalls. See BashFAQ- Why you don't read lines with "for" to understand why.
You can use xargs with -0 to read null delimited files and use the another find command without needing to use the for-loop
find ./ -type d -name "*foo*" -print0 | xargs -0 -I{.} find {.} -mindepth 2 -type d
The string following -I in xargs acts like a placeholder for the input received from the previous pipeline and passes it to the next command. The -print0 option is GNU specific which is a safe option to hande filenames/directory names containing spaces or any other shell meta-characters.
So with the above command in-place, if you are interested in doing some action over the output from 2nd command, do a process-substitution syntax with the while command,
while IFS= read -r -d '' f; do
echo "$f"
# Your other actions can be done on "$f" here
done < <(find ./ -type d -name "*foo*" -print0 | xargs -0 -I{.} find {.} -mindepth 2 -type d -print0)
As far the reason why your pipelines using find won't work is that you are not reading the previous find command's output. You needed either xargs or -execdir while the latter is not an option I would recommend.

Error 'rm: missing operand' when using along with 'find' command

I see that this question is getting popular.
I answered my own question below.
What says Inian is correct and it helped me to analyze my source code better.
My problem was in the FIND and not in the RM. My answer gives a block of code, which I am currently using, to avoid problems when FIND finds nothing but still would pass arguments to RM, causing the error mentioned above.
OLD QUESTION BELOW
I'm writing many and many different version of the same command.
All, are executed but with an error/info:
rm: missing operand
Try 'rm --help' for more information.
These are the commands I'm using:
#!/bin/bash
BDIR=/home/user/backup
find ${BDIR} -type d -mtime +180 -print -exec rm -rf {} \;
find ${BDIR} -type d -mtime +180 -print -exec rm -rf {} +
find "$BDIR" -type d -mtime +180 -print -exec rm -rf {} \;
find "$BDIR" -depth -type d -mtime +180 -print -exec rm -rf {} \;
find ${BDIR} -depth -type d -mtime +180 -print -exec rm -rf {} +
find $BDIR -type d -mtime +180 -print0 | xargs -0 rm -rf
DEL=$(FIND $BDIR -type d -mtime +180 -print)
rm -rf $DEL
I'm sure all of them are correct (because they all do their job), and if I run them manually I do not get that message back, but while in a .sh script I do.
EDIT: since I have many of these RM's, the problem could be somewhere else. I'm checking all of them. All of the above codes works but the best answer is the one marked ;)
The problem is when using find/grep along with xargs you need to be sure to run the piped command only if the previous command is successful. Like in the above case, if the find command does not produce any search results, the rm command is invoked with an empty argument list.
The man page of xargs
-r Compatibility with GNU xargs. The GNU version of xargs runs the
utility argument at least once, even if xargs input is empty, and
it supports a -r option to inhibit this behavior. The FreeBSD
version of xargs does not run the utility argument on empty
input, but it supports the -r option for command-line compatibil-
ity with GNU xargs, but the -r option does nothing in the FreeBSD
version of xargs.
Moreover, you don't to try all the commands like you pasted the below simple one will suit your need.
Add the -r argument to xargs like
find "$BDIR" -type d -mtime +180 -print0 | xargs -0 -r rm -rf
-f option of rm suppresses the rm: missing operand error:
-f, --force
ignore nonexistent files and arguments, never prompt
After researches, the command I'm comfortable using is:
HOME=/home/user
FDEL=$HOME/foldersToDelete
BDIR=/backup/my_old_folders
FLOG=/var/log/delete_old_backup.log
find ${BDIR} -mindepth 1 -daystart -type d -mtime +180 -printf "%f\n" > ${FDEL}
if [[ $? -eq 0 && $(wc -l < ${FDEL}) -gt 0 ]]; then
cd ${BDIR}
xargs -d '\n' -a ${FDEL} rm -rf
LOG=" - Folders older than 180 were deleted"
else
LOG=" - There aren't folders older than 180 days to delete"
fi
echo ${LOG} >> ${FLOG}
Why? I search all the old folders I want to delete and print them all into a file, regardless for their naming with or without space. If the file is bigger than 0 byte this means that there are folder I want no more.
If your 'FIND' fails with a 'rm: missing operand', it probably isn't to search in the RM rather in the FIND itself.
A good way of removing the file using FIND, is the one I felt to share with you.

What's the difference between `\;` and `+` at the end of a find command?

These are bash commands that are used to convert tabs to spaces.
Here's the link to the original stackoverflow post.
This one uses \; at the end of the command
find /path/to/directory -type f -iname '*.js' -exec sed -ie 's|\t| |g' '{}' \;
This one uses + instead of \;.
find /path/to/directory -type f -iname '*.js' -exec sed -ie 's|\t| |g' '{}' '+'
What exactly is the difference between the two?
The \; or + is not related to bash. It's an argument to the find command, specifically to find's -exec option.
find -exec uses {} to pass the current file name to the specified command, and \; to mark the end of the the command's arguments. The \ is needed because ; by itself is special to bash; by typing \;, you can pass a literal ; character as an argument. (You can also type ';' or ";".)
The + symbol (no \ needed because + is not special to bash) causes find to invoke the specified command with multiple arguments rather than just once, in a manner similar to xargs.
For example, suppose the current directory contains 2 files named abc and xyz. If you type:
find . -type f -exec echo {} \;
it invokes the echo command twice, producing this output:
./abc
./xyz
If you instead type:
find . -type f -exec echo {} +
then find invokes echo just once, with the following output:
./xyz ./abc
For more information, type info find or man find (if the documentation is installed on your system), or you can read the manual online at http://www.gnu.org/software/findutils/manual/html_node/find_html/

Resources