2

I need to make a bash script that takes two arguments, one is the string to search for in the filenames and the next one is the file path to search in. Next it needs to go through one matching file at a time and print it out, then prompt the user if they want to delete it or not.

An example of it would be:

./scriptName.sh foo /path/to/directory/

/path/to/directory/foo.txt

Delete this? (y/n)

user input

/path/to/directory/foop.txt

Delete this? (y/n)

user input

etc...

I originally tried

find $2 -type f -name "*$1*" -print

and

find $2 -type f -name "*$1*" -delete

Where $1 is the first argument and $2 are the second argument of the script.

This worked until I realized that it had to list each found file separately and prompt to delete them which is a bit of problem since the previous two lines of code just deletes all the matching files at once.

Kusalananda
  • 333,661

2 Answers2

2

You could either use -ok to execute rm on the found pathnames, or use -exec to execute rm -i.

This shows short examples of each, and assumes that $searchpath is the search path and $pattern is the final name pattern (e.g. "*$1*").

Using -ok: The -ok action of find is similar to -exec, but it asks the user for confirmation first, before executing the given utility. We use it to execute rm on each found pathname individually.

find "$searchpath" -type f -name "$pattern" -ok rm {} \;

Using rm -i: This would make the rm utility itself ask for confirmation before removing a file. We can use this on batches of pathnames.

find "$searchpath" -type f -name "$pattern" -exec rm -i {} +

You could obviously implement your own prompting too. Here's a bash script that prompts for deletion of the single file at "$pathname":

read -p "Delete the file at $pathname? y/[n]: "
[[ "$REPLY" == [yY]* ]] && rm "$pathname"

Calling this from find for batches of pathnames:

find "$searchpath" -type f -name "$pattern" -exec bash -c '
    for pathname do
        read -p "Delete the file at $pathname? y/[n]: "
        [[ "$REPLY" == [yY]* ]] && rm "$pathname"
    done' bash {} +

If the user replies with a string starting with an upper or lower cased y, the file will be deleted.

Related:

Kusalananda
  • 333,661
-2
pattern=$1
dir=$2

for file in $(find ${dir} -type f -name "*${pattern}*")
do
  /bin/rm -i ${file}
done

Basically you are looping through the files the find command returns and rm -i command is asking you a y/n question to delete the file or not. I think it is what you are looking for but if not, let people know what your exact need is for further help.

MelBurslan
  • 6,966
  • thank you for your help, I'm sure I can figure it out myself but the major part of this is the prompt to the user to decide whether or not to delete the file. Can I put this between the do done? – user276019 Apr 10 '16 at 04:53
  • rm -i command i.e., interactive rm is there for this exact purpose. You do not need to do anything, unless you want a totally different verbiage. If this is the case, put your message before rm command and test if the user said yes or not using conditionals. Just create a dummy file and run rm -i dummyfile command to see how it works – MelBurslan Apr 10 '16 at 04:57
  • 2
    Don’t do for file in $(find …); do …; do find "$dir" -type f -name "*${pattern}*" -exec rm -i {} +. Note that $dir should be quoted (in double quotes); putting it in braces accomplishes nothing. – G-Man Says 'Reinstate Monica' Apr 10 '16 at 05:31
  • @G-Man: putting variables in between { } is the proper way to do, and at the most unexpected moment, they save you a lot of headache, because you did not think how a variable changes or not changes when it is not quoted and comes next to some other variable or keyword. You are entitled to your opinion how to handle a case like this but please do not spread wrong beliefs of your like braces accomplish nothing – MelBurslan Apr 10 '16 at 06:13
  • 2
    @G-Man is correct. in this particular case, the braces, while harmless, do nothing of any use, but double-quoting the variables would protect against embedded spaces and other problem characters. He's also correct about not using for file in $(find ...). Use find ... -exec rm -i {} + as he suggested or find ... | while IFS= read file ; do rm -i "$file" ; done. The former (-exec rm) works for all files, even those with newlines in the filename, whereas the second (while loop) works for all filenames except those with newlines. These are not matters of opinion, they are facts. – cas Apr 10 '16 at 08:49
  • 3
  • I didn’t mean to say that enclosing variable names in braces never does anything.  Of course, it sometimes does something beneficial — in fact, "*${pattern}*" is an example (albeit a flawed one) of what it’s good for — and it’s not a bad habit to get into.  But it doesn’t accomplish anything when used on a free-standing variable, like ${file} in your answer.  … (Cont’d) – G-Man Says 'Reinstate Monica' Apr 11 '16 at 05:33
  • (Cont’d) …  On the other hand, all shell variable references (including $(…)) should always be quoted (unless you have a good reason not to); and doing that is a *much* more important habit to get into.  And thinking that enclosing variable names in braces gives you the same sort of protection that double quotes give you is wrong, and a very dangerous way of thinking.  … (Cont’d) – G-Man Says 'Reinstate Monica' Apr 11 '16 at 05:34
  • 1
    (Cont’d) …  You say, “at the most unexpected moment, they (braces) save you a lot of headache …”  I’m going to disagree with that.  If you use $foo_bar (or even "$foo_bar") when you meant ${foo}_bar (or "${foo}_bar" or "$foo"_bar), i.e., you want the value of $foo + the literal string _bar, and you do any testing, you will discover your error immediately.  But if you use $foo (or even ${foo}) when you meant "$foo" (or "${foo}"), that error will cause you a lot of headache at the most unexpected moment; when you encounter a filename with special character(s) in it. – G-Man Says 'Reinstate Monica' Apr 11 '16 at 05:35