How to avoid error-message during the execution of a bash script? - linux

I need to write a bash-script which will find all files with name string.h on the computer and copy them to some folder. My code is here:
#!/bin/bash
sudo find / -type f -name "string.h" -exec cp {} $HOME/MyDocuments \;
But during the execution of the script, I get error-messages on my console terminal "permission denied". How I can avoid getting this message? Console terminal must be clear.

Suppress the error messages from stderr(2) to the NULL stream designated by /dev/null
sudo find / -type f -name "string.h" -exec cp "{}" $HOME/MyDocuments \; 2 > /dev/null
where the 2 in the above line stands for file descriptor.
I would personally recommend you to investigate the actual cause of the issue rather than suppressing it. Do the above only if you are absolutely sure that the errors are trivial.

Related

Bash find- is showing the files but returning no such file or directory

I have a bash script I cannot get working. I am a dead set beginner in bash this is actually the first script I've ever used. I'm trying to get omxplayer to play a list of files in a directory. When the script runs I get feedback showing the file then the error that there is no such file or directory. Please help me?
#!/bin/sh
find /media/pi/88DC-E668/MP3/ -name "*.mp3" -exec PLAY={} \;; omxplayer "$PLAY";
This is the echo:
find: `PLAY=/media/pi/88DC-E668/MP3/Dance.mp3': No such file or directory
find: `PLAY=/media/pi/88DC-E668/MP3/Whitemary.mp3': No such file or directory
find: `PLAY=/media/pi/88DC-E668/MP3/Limo.mp3': No such file or directory
find: `PLAY=/media/pi/88DC-E668/MP3/Silo.mp3': No such file or directory
File "" not found.
Easy way:
find /media/pi/88DC-E668/MP3 -name \*.mp3 -exec omxplayer {} \;
or
while IFS= read -r -d '' mp3
do
omxplayer "$mp3"
done < <(find /media/pi/88DC-E668/MP3 -name \*.mp3 -print0)
or
find /media/pi/88DC-E668/MP3 -name \*.mp3 -print0 | xargs -0 -n1 omxplayer
You can omit the -n1 if the omxplayer could handle multiple filenames. In such case the 1st could be written as:
find /media/pi/88DC-E668/MP3 -name \*.mp3 -exec omxplayer {} +
but the simplest probably will be
#shopt -s globstar #the default is on
for mp3 in /media/pi/88DC-E668/MP3/{,**/}*.mp3
do
omxplayer "$mp3"
done
EDIT I stand corrected, but won't delete the answer as you can also learn from the mistakes of others. See comment and rather use this answer :)
So please don't do it like this, as this is a typical "happy path" solution - meaning: it works if you know what you're doing and you know your paths (e.g. that they don't contain spaces). I keep forgetting that many people don't know yet that spaces in paths are evil.
Just use xargs to pass what you found to your player like this:
#!/bin/sh
find /media/pi/88DC-E668/MP3/ -name "*.mp3" | xargs omxplayer
The -exec foo part means run the command foo for each path found.
In your case, -exec PATH={}, the {} part is replaced with the path name, ending up with something like -exec PATH=/media/pi/88DC-E668/MP3/Dance.mp3, and so then find tries to run the command PATH=/media/pi/88DC-E668/MP3/Dance.mp3 which fails because there isn't actually any such program to execute.
xargs is the usual way to do what you're trying to do, as described in another comment already.
You could also do:
find /media/pi/88DC-E668/MP3/ -name \*.mp3 |
while read f; do
omxplayer "$f"
done

bash find -exec sometimes works and sometimes doesn't

I'm probably missing something, but this oneliner in a bash-script to cycle through some scripts that dump data from different sources:
find . -name 'dump-*.sh' -exec {} "$DUMP_LOG" &>>"$DUMP_LOG" \;
will work when I execute the bash-script containing this oneliner directly, but it doesn't work when I invoke it as the cmd_preexec in rsnapshot. It doesn't spawn any errors, it just doesn't do anything.
I tried adding '(/bin/)bash -c', like this:
find . -name 'dump-*.sh' -exec bash -c '{} "$DUMP_LOG" &>>"$DUMP_LOG"' \;
but then I get an error about '(/bin/)bash not existing, even if Irun the script directly.
OK, silly me. Of course the first parameter of the find-cmd needs the working directory.
find /usr/local/sbin -name 'dump-*.sh' -exec {} "$DUMP_LOG" &>>"$DUMP_LOG" \;
has solved the problem.

find command - syntax with -exec handle

Background and problem:
I am using a find command to gzip all files that match my criteria:
sudo -u userAccount -i find /apps/server/userAccount/logs/. -maxdepth 1 -type f -exec gzip {} \;
This will go ahead and compress all files against my criteria but I don't want to be prompted to overwrite -- I want to be able to automagically skip and file that already exists as I'm dealing with sensitive data and cannot risk losing anything.
So far, I have tried:
statement as above -exec yes n | gzip {} \;
statement as above -exec [an if statement to determine if a file already exists]
None of these work due to find statement syntax errors.
Question:
Does anybody know how I can use either method above with my find statement?
Update:
I attempted to use:
sudo -u userAccount -i find /apps/server/userAccount/logs/. -maxdepth 1 -type f -exec bash -c "yes n | gzip {}" \;
But I just get scrolling 'y' characters returned to the console until I CTRL-C. Any suggestions?
The command after -exec is not run by a shell, it's executed directly by find, so it can't use shell metacharacters or builtins. If you need to use the shell, you have to invoke it explicitly:
-exec bash -c "yes n | gzip {}" \;
If you don't want to use pipes to the gzip command, you can use a case to test your file type.
find $PATH_TO_LOGS -maxdepth 1 -mtime +$SOME_NUMBER_OF_DAYS -exec sh -c "case {} in *.gz) ;; *) gzip '{}' ;; esac;" \;
Of course, this depends upon your file names ending in the traditional .gz extension. Benefits of this is that you can also process other file types by extension in this form.
Also, if you really don't want to lose anything, and you can't depend upon a backup copy, it might be a good idea to instead timestamp the files and simply incur the storage space hit.
find $PATH_TO_LOGS -maxdepth 1 -mtime +$SOME_NUMBER_OF_DAYS -exec gzip -N {} \;

Find files in a dir, executing a command with execdir and redirecting

It seems like I am unable to find a direct answer to this question.
I appreciate your help.
I'm trying to find all files with a specific name in a directory, read the last 1000 lines of the file and copy it in to a new file in the same directory. As an example:
Find all files names xyz.log in the current directory, copy the last 1000 lines to file abc.log (which doesn't exist).
I tried to use the following command with no luck:
find . -name "xyz.log" -execdir tail -1000 {} > abc.log \;
The problem I'm having is that for all the files in the current directory, they all write to abc.log in the CURRENT directory and not in the directory where xyz.log resides. Clearly the find with execdir is first executed and then the output is redirected to abc.log.
Can you guys suggest a way to fix this? I appreciate any information/help.
EDIT- I tried find . -name "xyz.log" -execdir sh -c "tail -1000 {} > abc.log" \; as suggested by some of the friends, but it gives me this error: sh: ./tail: No such file or directory error message. Do you guys have any idea what the problem is?
Luckily the solution to use -printf is working fine.
The simplest way is this:
find . -name "xyz.log" -execdir sh -c 'tail -1000 "{}" >abc.log' \;
A more flexible alternative is to first print out the commands and then execute them all with sh:
find . -name "xyz.log" -printf 'tail -1000 "%p" >"%h/abc.log"\n' | sh
You can remove the | sh from the end when you're trying it out/debugging.
There is a bug in some versions of findutils (4.2 and 4.3, though it was fixed in some 4.2.x and 4.3.x versions) that cause execdir arguments that contain {} to be prefixed with ./ (instead of the prefix being applied only to {} it is applied to the whole quoted string). To work around this you can use:
find . -name "xyz.log" -execdir sh -c 'tail -1000 "$1" >abc.log' sh {} \;
sh -c 'script' arg0 arg1 runs the sh script with arg0, arg1, etc. passed to it. By convention, arg0 is the name of the executable (here, "sh"). From the script you can access the arguments using $0 (corresponding to "sh"), $1 (corresponding to find's expansion of {}), etc.
The redirect isn't passed into execdir, so abc.log shows up in the directory you run the command in. -execdir also doesn't like embedded redirects. but you can workaround the problem by passing -execdir a shell command with a redirect embedded, like this:
find . -name "xyz.log" -execdir sh -c '/usr/bin/tail -1000 {} > abc.log' \;
Much credit to this blog post (not mine):
http://www.microhowto.info/howto/act_on_all_files_in_a_directory_tree_using_find.html
Edit
I put the full path to tail in the command (assuming it's in /usr/bin on your system), since sh may load a .profile with a PATH that differs from your current shell.
Here's another non-find (well, sorta - it still uses find but doesn't try to shoehorn find into doing the whole thing):
while read f
do
d=$(dirname "${f}")
tail -n 1000 "${f}" > "${d}/abc.log"
done < <(find . -type f -name xyz.log -print)

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 {} \;

Resources