grep and sed with spaces in filenames
Add the -Z
(aka --null
) flag to grep
, and the -0
(also aka --null
) flag to xargs
.
This will output NUL terminated file names, and tell xargs
to read NUL terminated arguments.
eg.
grep -irlZ $schema $WORKDIR/ | xargs -0 sed -i 's/'"$schema"'/EXI1/gI'
grep files with whitespaces in filename
Quote $i
cat filelist | while read i; do grep "pattern" "$i"; done
grep the filename using sed
echo "result_0.01.dat resultFile" | sed "s|.*.dat|someOtherText.dat|g"
what happens here in sed part is:
we substitude (s flag at the beginning of sed)
we find everything till .dat. We replace that part with someOtherText.
and we do it globally (g flag at the end)
grep cannot read filename after find folders with spaces
The issue is that your array contains filenames surrounded by literal "
quotes.
But worse, find
's -exec cmd {} \;
executes cmd
separately for each file which can be inefficient. As mentioned by @TomFenech in the comments, you can use -exec cmd {} +
to search as many files within a single cmd
invocation as possible.
A better approach for recursive search is usually to let find
output filenames to search, and pipe its results to xargs
in order to grep inside as many filenames together as possible. Use -print0
and -0
respectively to correctly support filenames with spaces and other separators, by splitting results by a null character instead - this way you don't need quotes, reducing possibility of bugs.
Something like this:
find . -type f -not -path './.git/*' -print0 | xargs -0 egrep '(GNU)'
However in your question you had grep -q
in a loop, so I suspect you may be looking for an error status (found/not found) for each file? If so, you could use -l
instead of -q
to make grep
list matching filenames, and then pipe/send that output to where you need the results.
find . -print0 | xargs -0 egrep -l pattern > matching_filenames
Also note that grep -E
(or egrep
) uses extended regular expressions, which means parentheses create a regex group. If you want to search for files containing (GNU)
(with the parentheses) use grep -F
or fgrep
instead, which treats the pattern as a string literal.
Make xargs handle filenames that contain spaces
The xargs
command takes white space characters (tabs, spaces, new lines) as delimiters.
You can narrow it down only for the new line characters ('\n') with -d
option like this:
ls *.mp3 | xargs -d '\n' mplayer
It works only with GNU xargs.
For MacOS:
ls *.mp3 | tr \\n \\0 | xargs -0 mplayer
The more simplistic and practically useful approach (when don't need to process the filenames further):
mplayer *.mp3
Find and xargs to correctly handle filenames with spaces in their names
find . -name "*.php" -print0 | xargs -0 sed -i 's/string1/string2/g'
You can also do it without xargs at all:
find . -name "*.php" -execdir sed -i 's/string1/string2/g' {} +
Bash and filenames with spaces
Try this:
(IFS=$'\n'; grep -li 'regex' $(<listOfFiles.txt))
IFS
is the Internal Field Separator. Setting it to $'\n'
tells Bash to use the newline character to delimit filenames. Its default value is $' \t\n'
and can be printed using cat -etv <<<"$IFS"
.
Enclosing the script in parenthesis starts a subshell so that only commands within the parenthesis are affected by the custom IFS
value.
Related Topics
Is Visual Basic Supported by .Net Core on Linux
Dependency Failure While Installing Libboost-All-Dev on Ubuntu Core 14.04
X11 Forwarding of Gui App in Docker Container
How Syscall Knows Where to Jump
How to Find Files Containing a String Using Egrep
Copy N Bytes of Data X to File
Bash Script: Can Not Properly Handle Sigtstp
Git Error: Gpg Failed to Sign The Data on Linux
How to Give Password in Shell Script
X86 Assembly, Little Endianness Not Being Followed(Or Is It) (Linux)
Change Conda Default Pkgs_Dirs and Envs Dirs
Unix Unzip: How to Batch Unzip Zip Files in a Folder and Save in Subfolders