The problem/use-case that the feature addresses

When using the "--scan" option, redis-cli uses a newline as the default separator between the keys it outputs. This is not ideal, since a key name may contain a newline character. This is especially problematic when piping the output of redis-cli to "xargs", where it would be natural to use the "-0" option to separate items.

Description of the feature

Add a new command line option ("--print0") to redis-cli. This option will have a similar affect to the "-print0" option of the "find" command, which causes it to separate its outputs using a null byte instead of a newline.

Naming the argument "print0" should make it recognizable to anyone familiar with the "find" command.

Alternatives you've considered

I could not think of an alternative as it is not possible to determine from the "scan" output whether two lines represent two keys or a single key that contains a newline character. The following output:

abc def

...could be two keys ("abc", "def") or it could be a single key "abc\ndef".

Additional information

As far as I can tell this should be a relatively straightforward addition to redis-cli.c. I have code ready to go if there is interest in this feature.

Comment From: zuiderkwast

This seems like a useful feature. Out of curiosity, I looked up which options are used for this by various commands.

GNU Coreutils:

  • -z, --zero-terminated is used by head, tail, sort, shuf, uniq, comm, cut, paste, join and numfmt. Some of these also accept -t '\0'.
  • -z, --zero is used by md5sum, readlink, basename, dirname, realpath and id.
  • -0, --null is used by du, printenv and env.

GNU Findutils:

  • --print0 is used by find.
  • -0, --null is used by locate and xargs.

Comment From: jaypatrickhoward

Given most users will be using this option to pipe output to xargs, it may make more sense to copy the xargs syntax instead of what I originally proposed. I only chose the "find" syntax because that's where I see it used most often (to pipe to xargs).

Comment From: itamarhaber

I pity people who use newlines in their key names, this issue probably being the least of their problems ;) In the same spirit, I'd like to point that there may another set (subset? intersection of sorts?) of pitiful users who use other absurd characters in their key names (e.g. NULL), so having --print0 would still fail them.

The cli does have a way around this in non---scan mode, namely the --raw and --non-raw switches. Consider this:

$ redis-cli
127.0.0.1:6379> SET "new\x0Aline" foo
OK
127.0.0.1:6379> SET "null\xffteminator" bar
OK
127.0.0.1:6379> QUIT
$ redis-cli --raw keys "*"
null�teminator
new
line
$ redis-cli --no-raw keys "*"
1) "null\xffteminator"
2) "new\nline"

IMO, it would be better if --scan would also honor these. Would that address the problem with shell output pipelining?

Comment From: jaypatrickhoward

Would that address the problem with shell output pipelining?

Maybe! I'll experiment with raw/no-raw (using keys instead of scan) and see if it works. If so, I'll shift gears to having --scan honor them.

I actually didn't anticipate that it would be possible to use a null byte into a key name; figured they were required to be valid Unicode strings and not arbitrary sequences of bytes.

Comment From: itamarhaber

I actually didn't anticipate that it would be possible to use a null byte into a key name; figured they were required to be valid Unicode strings and not arbitrary sequences of bytes.

Redis' strings are "binary-safe", which basically means anything goes (or Garbage-In-Garbage-Out). The server can accept any byte array, including an empty one, as a valid string. Key names, string values and the nested values of most data types in Redis are like this.

Comment From: jaypatrickhoward

Using @yossigo's branch that enables --no-raw behavior for --scan, I'm still not able to delete keys with a newline by piping to xargs:

% ./redis-cli --scan
"new\nline"
% ./redis-cli --scan | xargs -n1 ./redis-cli del      
(integer) 0
(integer) 0

A -0 option in conjunction with --scan --no-raw would do the trick (for both keys containing a null byte and those containing a newline) but maybe this is possible by fiddling around w/ xargs?

Comment From: yossigo

Because Redis is binary safe, any alternative-delimiter approach will have limited usability in some cases. But as redis-cli already has its own binary-safe notation, which just need to use it more.

It is already applied to input in TTY mode, i.e. this works:

redis-cli
127.0.0.1:6379> del "new\nline"

We need a flag to make that work also in non-TTY mode:

./redis-cli --scan --no-raw | xargs -n1 ./redis-cli --no-raw-input del

Comment From: yossigo

Fixed in #8566