Easiest way to escape the output of find? - shell

I want to do something like
find . -name "*whatever*" | xargs zip my.zip
but if the files I'm finding contain certain characters, this gets messed up, for example with spaces within the filenames. I guess I should escape the results. I couldn't quite understand from man find whether it could do this for me. So:
Can I make find escape the results?
If not, how should I escape them?

Null separation was made for this exact case.
find can be instructed to separate its outputs with NUL characters (0's) via the -print0 option.
xargs can be instructed that its incoming arguments will be NUL separated with the -0 option.
Hence,
find . -name "*whatever*" -print0 | xargs -0 zip my.zip

Related

Given a text file with file names, how can I find files in subdirectories of the current directory?

I have a bunch of files with different names in different subdirectories. I created a txt file with those names but I cannot make find to work using the file. I have seen posts on problems creating the list, on not using find (do not understand the reason though). Suggestions? Is difficult for me to come up with an example because I do not know how to reproduce the directory structure.
The following are the names of the files (just in case there is a formatting problem)
AO-169
AO-170
AO-171
The best that I came up with is:
cat ExtendedList.txt | xargs -I {} find . -name {}
It obviously dies in the first directory that it finds.
I also tried
ta="AO-169 AO-170 AO-171"
find . -name $ta
but it complains find: AO-170: unknown primary or operator
If you are trying to ask "how can I find files with any of these names in subdirectories of the current directory", the answer to that would look something like
xargs printf -- '-o\0-name\0%s\0' <ExtendedList.txt |
xargs -r0 find . -false
The -false is just a cute way to let the list of actual predicates start with "... or".
If the list of names in ExtendedList.txt is large, this could fail if the second xargs decides to break it up between -o and -name.
The option -0 is not portable, but should work e.g. on Linux or wherever you have GNU xargs.
If you can guarantee that the list of strings in ExtendedList.txt does not contain any characters which are problematic to the shell (like single quotes), you could simply say
sed "s/.*/-o -name '&'/" ExtendedList.txt |
xargs -r find . -false

using find with variables in bash

I am new to bash scripting and need help:
I need to remove specific files from a directory . My goal is to find in each subdirectory a file called "filename.A" and remove all files that starts with "filename" with extension B,
that is: "filename01.B" , "filename02.B" etc..
I tried:
B_folders="$(find /someparentdirectory -type d -name "*.B" | sed 's# (.*\)/.*#\1#'|uniq)"
A_folders="$(find "$B_folders" -type f -name "*.A")"
for FILE in "$A_folders" ; do
A="${file%.A}"
find "$FILE" -name "$A*.B" -exec rm -f {}\;
done
Started to get problems when the directories name contained spaces.
Any suggestions for the right way to do it?
EDIT:
My goal is to find in each subdirectory (may have spaces in its name), files in the form: "filename.A"
if such files exists:
check if "filename*.B" exists And remove it,
That is: remove: "filename01.B" , "filename02.B" etc..
In bash 4, it's simply
shopt -s globstar nullglob
for f in some_parent_directory/**/filename.A; do
rm -f "${f%.A}"*.B
done
If the space is the only issue you can modify the find inside the for as follows:
find "$FILE" -name "$A*.B" -print0 | xargs -0 rm
man find shows:
-print0
True; print the full file name on the standard output, followed by a null character (instead of the newline character that -print uses). This allows
file names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output. This option corre-
sponds to the -0 option of xargs.
and xarg's manual
-0 Input items are terminated by a null character instead of by whitespace, and the quotes and backslash are not special (every character is taken literal-
ly). Disables the end of file string, which is treated like any other argument. Useful when input items might contain white space, quote marks, or
backslashes. The GNU find -print0 option produces input suitable for this mode.

concat a lot of files to stdout

I have a large number of files in directory - ~100k. I want to combine them and pipe them to standard output (I need that to upload them as one file elsewhere), but cat $(ls) complains that -bash: /bin/cat: Argument list too long. I know how to merge all those files into a temporary one, but can I just avoid it?
For a start, cat $(ls) is not the right way to go about this - cat * would be more appropriate. If the number of files is too high, you can use find like this:
find -exec cat {} +
This combines results from find and passes them as arguments to cat, executing as many separate instances as needed. This behaves much in the same way as xargs but doesn't require a separate process or the use of any non-standard features like -print0, which is only supported in some versions of find.
find is recursive by default, so you can specify a -maxdepth 1 to prevent this if your version supports it. If there are other things in the directory, you can also filter by -type (but I guess there aren't, based on your original attempt).
find . -type f -print0 |xargs -0 cat
xargs will invoke cat several times, each time with as many arguments as it can fit on the command line (the combined length of the args can be no more than getconf ARG_MAX).
-print0 (seperate files with \0) for find in combination with -0 (process files separated with \0) for xargs is just a good habit to follow as it will prevent the commands from breaking on filenames with special or white characters in them.

List files matching pattern when too many for bash globbing

I'd like to run the following:
ls /path/to/files/pattern*
and get
/path/to/files/pattern1
/path/to/files/pattern2
/path/to/files/pattern3
However, there are too many files matching the pattern in that directory, and I get
bash: /bin/ls: Argument list too long
What's a better way to do this? Maybe using the find command? I need to print out the full paths to the files.
This is where find in combination with xargs will help.
find /path/to/files -name "pattern*" -print0 | xargs -0 ls
Note from comments: xargs will help if you wish to do with the list once you have obtained it from find. If you only intend to list the files, then find should suffice. However, if you wish to copy, delete or perform any action on the list, then using xargs instead of -exec will help.

loop to move files not working

I am having one of these mornings where nothing goes to plan. I need to move files to a target directory by chunks of 1,000 at time
I wanted to loop thru my files like so
for i in `find . -name '*XML'`
for((b=0; b<1000; b++))
do
mv $i targetdirect/
done
done
But I get a "-bash: syntax error near unexpected token `done:" error.
What I am missing??
The second for loop is a syntax error. Also you should double-quote "$i".
What do you mean by moving 1000 files at a time? Something like this perhaps?
find . -name '*.XML' -print0 | xargs -r0 -n 1000 mv -t targetdirect
The -print0 and corresponding xargs -0 are a GNU extension to handle arbitrary file names. This works because the null character is an invalid character in file names on Unix; hence, it is safe to use as a delimiter between file names. For regularly named files (no quotes, no newlines etc in the file names) this may seem paranoid, but it is well-documented practice and a FAQ.
Your first for loop has no corresponding do (You have two done, but only one do.)

Resources