The GNU implementation of ls
has a --quoting-style
options which let you specify a quoting style, but none of them is compatible with the quoting style expected by xargs
.
xargs
(introduced by PWB Unix in the late 70s) quoting style is similar to that of the Mashey shell (also from PWB Unix), a predecessor of the Bourne shell.
ls --quoting-style=shell-always
quotes with single quotes as per the Bourne/POSIX shell style, with single quotes themselves entered as \'
. xargs
supports both '...'
and "..."
in addition to backslash but quoted strings cannot include newline characters which can only be escaped with backslash.
The POSIX xargs
specification also expects xargs
to parse the input as text in the current locale and also does not guarantee it will work for arguments larger than 256 bytes, so in general can't be used to process arbitrary data.
The really only reliable way to use xargs
is with the -0
option found in some implementations such as GNU xargs
(you also generally want to use the -r
option, also non-standard).
The next version of GNU ls
will have a --zero
option to output files NUL-delimited, so then you'll be able to do:
ls -t --zero | xargs -r0 printf ' * %s\n'
For instance.
For now, what you can do is use the --quoting-style=shell-always
mode to pass that list to a shell, and that shell to convert it to a NUL-delimited list:
(
echo 'files=('
ls --quoting-style=shell-always -t
echo '); print -rNC1 -- $files'
) | zsh | xargs -r0 some command
But if you're already using zsh
, you don't really need ls
nor xargs
as zsh
can sort its globs by other criteria like ls
and has a stat
builtin to retrieve file metadata (the only reasons you may want to use ls
) and its own zargs
command.
In any case, in:
ls -d -- *.txt
It's the shell that expands *.txt
to the list of matching files. ls
is pretty useless there as it just ends up printing them.
You might as well pass the glob expansion to something that can print the list NUL-delimited, such as the print
builtin in zsh
:
print -rNC1 -- *.txt(N)
(here also using the N
ullglob qualifier, so it prints nothing if the glob doesn't match any file).
Or in the bash
shell (sometimes still used on GNU systems as that's the GNU shell):
print0() {
[ "$#" -eq 0 ] || printf '%s\0' "$@"
}
shopt -s nullglob
print0 *.txt
rm -rf 2021*/"X y"/foobar
– muru Dec 19 '21 at 09:15