xargs
by default expects an input in a very specific format where arguments are meant to be separated by whitespace characters (the list of which depends on the xargs
implementation and the locale though in the case of GNU xargs
that's limited to space, tab and newline) and where single quotes double quotes and backslash can be used to escape those (though in its own unique way).
That's definitely not the format of the list of files output by grep -rl
.
With -d '\n'
(a GNU extension), the format changes to being newline delimited with no way to escape newline. That happens to be the format output by grep -rl
, but that still doesn't work for file names containing newline characters. The output of grep -rl
is simply not post-processable reliably for arbitrary file names.
GNU grep
(and you're already using a few GNUisms) has a -Z
/--null
option to output the list of files for -l
in a post-processable fashion which you can combine with xargs -r0
(also GNU extensions, but more portable than -d
):
grep -rlFIZ /home/user/.local . |
xargs -r0 sed -Ei 's|/home/user(/\.local)\>|~\1|g'
I've also removed the -i
option to grep
as file paths are case sensitive on most Unix-like systems including RHEL and you're not using the i
flag to sed
's s
anyway, added the -F
for fixed-string match (to avoid having to escape the .
regexp operator). Added \>
for word boundary so it doesn't match on the /home/user/.local
inside /home/user/.locale
for instance (though it will still match inside /home/user/.local-tmp
), explicitly specified which directory to r
ecursively look into (for clarity and compatibility with older versions of GNU grep
) and added some capturing, recalled with \1
.
Your problem has been explained by you having a grep='grep --color=yes'
alias instead of grep='grep --color=auto'
or grep='grep --color'
which means grep
outputs colouring escape sequences even when its output doesn't go directly to a terminal.
There are other possible explanations.
Files in ~/.local
are written by many applications, files are being added and remove at will by them; in between grep
finding files and sed
processing them, some of those files could very well have gone.
Also beware sed -i
doesn't really edit files i
n-place, it replaces them with an edited copy. If some applications currently have the files you're editing opened, they will still carry on seeing the original (now deleted) copy and any change they're making to them from now on will be lost.
sed -i
also breaks hard links and symlinks (though GNU grep -r
will not look into symlinks).
Replacing /home/user
with ~
is also often not a valid thing to do. Tilde expansion is something done by shells (most shells these days), and a few other applications, but certainly not all. Outside of those, ~/.local/file
is the path of the file
file inside the .local
subdir of a subdir of the current working directory called ~
(like after mkdir -p '~/.local' && touch '~/.local/file'
). So replacing /home/user/.local
with ~/.local
is likely to break many applications.
Also beware that GNU grep
-I
to skip binary files uses heuristics and is not foolproof (either way, can give false positives and false negatives). Some text-based formats like PHP data serialisation format will be broken if you replace a string with another one of a different length.
{path/file which is valid}
? What is the actual error? Does it work for some files? Have you tried making a test directory with just one or two files and running the command there? Does that work? You might also want to simplify yoursed
tosed -i 's|/home/user/\.local|~/.local|'
and do consider using an option for the-i
switch, something like-i .bak
to keep a backup of your files. At least test without the-i
switch first so you don't break things!foo bar
and you getsed
errors forfoo
and then forbar
?ls
the files.grep -iIlr "/home/user/.local"
command by itself.