Move only mp3s within multiple directories to a single folder - macos

This solution from SO works but unfortunately some of my mp3s have quotes (') in their names which results in the following error:
xargs: unterminated quote
Is it possible to adapt the following command to allow it to copy all mp3s, regardless of the quotes in their file name?
find . -name "*.mp3" | xargs -I {} cp -iv "{}" /my/dir
Thank you.

Try adding -print0 to find and -0 to xargs.

find . -name "*.mp3" -exec cp -iv {} /my/dir \;

Related

Iterate over find command's output with exec or xargs & pass this as paramater to script

I have to find files & execute a python command on them. There are several thousand files that find command finds. So, instead of running a for loop to iterate over the output of find command, I'm trying to do an xargs over find output.
I am unable to work out how to pass the output of find command as parameter to the script.
find "$DIRLOC" -iname "*.html" |
xargs python3 src/shell/updates/python/updateshere.py <filename from find command's output to go here> "$HASH" {} \;
Please could someone help me with this?
You can do it like this:
find "$DIRLOC" -iname "*.html" -print0 |
xargs -0 -I {} python3 src/shell/updates/python/updateshere.py {} "$HASH"
Used -print0 option in find and -0 in xargs to handle filename/directory names with space or other special characters.
You can handle all in find also using -exec option:
find "$DIRLOC" -iname "*.html" -exec \
python3 src/shell/updates/python/updateshere.py {} "$HASH" \;

I used the'-print0' option to handle filenames with spaces, but I get an error

#!/bin/bash
find /home/data -name '*QQ*' -print0 -exec bash -c ' mv $1 ${0/\-QQ/-TT}' {} \;
I used the'-print0' option to handle filenames with spaces, but I get an error
/home/data/gone to sea.1080p-QQ.mp4mv: target 'sea.1080p-TT.mp4' is not a directory
Which part is wrong?
Thanks
You don't need -print0, since you're not piping the output to another program.
You just need to quote properly in the bash command.
find /home/data -name '*-QQ*' -exec bash -c 'mv "$1" "${1/\-QQ/-TT}"' {} {} \;
This should work as long as the filenames don't contain double quote or $ characters.
You could also avoid bash -c by using the rename command:
find /home/data -name '*-QQ*' -exec rename 's/-QQ/-TT/' {} +

BASH: find and rename files & directories

I would like to replace :2f with a - in all file/dir names and for some reason the one-liner below is not working, is there any simpler way to achieve this?
Directory name example:
AN :2f EXAMPLE
Command:
for i in $(find /tmp/ \( -iname ".*" -prune -o -iname "*:*" -print \)); do { mv $i $(echo $i | sed 's/\:2f/\-/pg'); }; done
You don't have to parse the output of find:
find . -depth -name '*:2f*' -execdir bash -c 'echo mv "$0" "${0//:2f/-}"' {} \;
We're using -execdir so that the command is executed from within the directory containing the found file. We're also using -depth so that the content of a directory is considered before the directory itself. All this to avoid problems if the :2f string appears in a directory name.
As is, this command is harmless and won't perform any renaming; it'll only show on the terminal what's going to be performed. Remove echo if you're happy with what you see.
This assumes you want to perform the renaming for all files and folders (recursively) in current directory.
-execdir might not be available for your version of find, though.
If your find doesn't support -execdir, you can get along without as so:
find . -depth -name '*:2f*' -exec bash -c 'dn=${0%/*} bn=${0##*/}; echo mv "$dn/$bn" "$dn/${bn//:2f/-}"' {} \;
Here, the trick is to separate the directory part from the filename part—that's what we store in dn (dirname) and bn (basename)—and then only change the :2f in the filename.
Since you have filenames containing space, for will split these up into separate arguments when iterating. Pipe to a while loop instead:
find /tmp/ \( -iname ".*" -prune -o -iname "*:*" -print \) | while read -r i; do
mv "$i" "$(echo "$i" | sed 's/\:2f/\-/pg')"
Also quote all the variables and command substitutions.
This will work as long as you don't have any filenames containing newline.

Getting error "xargs unterminated quote" when tried to print the number of lines in terminal

I want to get the number of lines in my application. I am using this code:
find . "(" -name "*.m" -or -name "*.h" ")" -print | xargs wc -l
It is working fine in other applications but for one of my applications it is giving the error "xargs unterminated quote".
Does one of your filenames have a quote in it? Try something like this:
find . "(" -name "*.m" -or -name "*.h" ")" -print0 | xargs -0 wc -l
The -print0 argument tells find to use the NULL character to terminate each name that it prints out. The -0 argument tells xargs that its input tokens are NULL-terminated. This avoids issues with characters that otherwise would be treated as special, like quotes.
This can happen because you have a single quote in a filename somewhere...
i.e., -> '
To find the problem file, run the following in the terminal:
\find . | grep \'
you can also run xargs like so to effectively address this issue:
xargs -I
and it can also happen if you have an alias for xargs setup that's causing an issue. To test if this is the case, just run xargs with a \ in front of it, e.g.
\find . | \xargs ....
The \ simply means "run the command without any aliases"
The canonical way to solve quotes, spaces and special characters problems when using find is to use the -exec option instead of xargs.
For your case you can use:
find . "(" -name "*.m" -or -name "*.h" ")" -exec wc -l "{}" \;
After some tinkering, I found that this command worked for me (because I had spaces and unmatched quotations in my filenames):
find . -iname "*USA*" -exec cp "{}" /Directory/to/put/file/ \;
. refers to the location the search is being run
-iname followed by the expression refers to the match criteria
-exec cp "{}" /Directory/to/put/file/ \; tells the command to execute the copy command where each file found via -iname replaces "{}"
You need the \; to denote to the exec command that the cp statement is ending.
Solved by replacing from source " with \"

Shell Scripting: Using bash with xargs

I'm trying to write a bash command that will delete all files matching a specific pattern - in this case, it's all of the old vmware log files that have built up.
I've tried this command:
find . -name vmware-*.log | xargs rm
However, when I run the command, it chokes up on all of the folders that have spaces in their names. Is there a way to format the file path so that xargs passes it to rm quoted or properly escaped?
Try using:
find . -name vmware-*.log -print0 | xargs -0 rm
This causes find to output a null character after each filename and tells xargs to break up names based on null characters instead of whitespace or other tokens.
Do not use xargs. Find can do it without any help:
find . -name "vmware-*.log" -exec rm '{}' \;
Check out the -0 flag for xargs; combined with find's -print0 you should be set.
find . -name vmware-*.log -print0 | xargs -0 rm
GNU find
find . -name vmware-*.log -delete
find . -name vmware-*.log | xargs -i rm -rf {}
find -iname pattern
use -iname for pattern search
To avoid space issue in xargs I'd use new line character as separator with -d option:
find . -name vmware-*.log | xargs -d '\n' rm

Resources