]>
git.saurik.com Git - redis.git/blob - src/redis-cli.c
   1 /* Redis CLI (command line interface) 
   3  * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com> 
   6  * Redistribution and use in source and binary forms, with or without 
   7  * modification, are permitted provided that the following conditions are met: 
   9  *   * Redistributions of source code must retain the above copyright notice, 
  10  *     this list of conditions and the following disclaimer. 
  11  *   * Redistributions in binary form must reproduce the above copyright 
  12  *     notice, this list of conditions and the following disclaimer in the 
  13  *     documentation and/or other materials provided with the distribution. 
  14  *   * Neither the name of Redis nor the names of its contributors may be used 
  15  *     to endorse or promote products derived from this software without 
  16  *     specific prior written permission. 
  18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
  19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
  20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
  22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
  24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
  25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  28  * POSSIBILITY OF SUCH DAMAGE. 
  48 #include "linenoise.h" 
  53 #define REDIS_NOTUSED(V) ((void) V) 
  55 #define OUTPUT_STANDARD 0 
  59 static redisContext 
*context
; 
  60 static struct config 
{ 
  73     int cluster_reissue_command
; 
  77     int stdinarg
; /* get last arg from stdin. (-x option) */ 
  79     int output
; /* output mode, see OUTPUT_* defines */ 
  86 char *redisGitSHA1(void); 
  87 char *redisGitDirty(void); 
  89 /*------------------------------------------------------------------------------ 
  91  *--------------------------------------------------------------------------- */ 
  93 static long long mstime(void) { 
  97     gettimeofday(&tv
, NULL
); 
  98     mst 
= ((long)tv
.tv_sec
)*1000; 
  99     mst 
+= tv
.tv_usec
/1000; 
 103 static void cliRefreshPrompt(void) { 
 106     if (config
.hostsocket 
!= NULL
) 
 107         len 
= snprintf(config
.prompt
,sizeof(config
.prompt
),"redis %s", 
 110         len 
= snprintf(config
.prompt
,sizeof(config
.prompt
),"redis %s:%d", 
 111                        config
.hostip
, config
.hostport
); 
 112     /* Add [dbnum] if needed */ 
 113     if (config
.dbnum 
!= 0) 
 114         len 
+= snprintf(config
.prompt
+len
,sizeof(config
.prompt
)-len
,"[%d]", 
 116     snprintf(config
.prompt
+len
,sizeof(config
.prompt
)-len
,"> "); 
 119 /*------------------------------------------------------------------------------ 
 121  *--------------------------------------------------------------------------- */ 
 123 #define CLI_HELP_COMMAND 1 
 124 #define CLI_HELP_GROUP 2 
 132     /* Only used for help on commands */ 
 133     struct commandHelp 
*org
; 
 136 static helpEntry 
*helpEntries
; 
 137 static int helpEntriesLen
; 
 139 static sds 
cliVersion() { 
 141     version 
= sdscatprintf(sdsempty(), "%s", REDIS_VERSION
); 
 143     /* Add git commit and working tree status when available */ 
 144     if (strtoll(redisGitSHA1(),NULL
,16)) { 
 145         version 
= sdscatprintf(version
, " (git:%s", redisGitSHA1()); 
 146         if (strtoll(redisGitDirty(),NULL
,10)) 
 147             version 
= sdscatprintf(version
, "-dirty"); 
 148         version 
= sdscat(version
, ")"); 
 153 static void cliInitHelp() { 
 154     int commandslen 
= sizeof(commandHelp
)/sizeof(struct commandHelp
); 
 155     int groupslen 
= sizeof(commandGroups
)/sizeof(char*); 
 159     helpEntriesLen 
= len 
= commandslen
+groupslen
; 
 160     helpEntries 
= malloc(sizeof(helpEntry
)*len
); 
 162     for (i 
= 0; i 
< groupslen
; i
++) { 
 164         tmp
.argv 
= malloc(sizeof(sds
)); 
 165         tmp
.argv
[0] = sdscatprintf(sdsempty(),"@%s",commandGroups
[i
]); 
 166         tmp
.full 
= tmp
.argv
[0]; 
 167         tmp
.type 
= CLI_HELP_GROUP
; 
 169         helpEntries
[pos
++] = tmp
; 
 172     for (i 
= 0; i 
< commandslen
; i
++) { 
 173         tmp
.argv 
= sdssplitargs(commandHelp
[i
].name
,&tmp
.argc
); 
 174         tmp
.full 
= sdsnew(commandHelp
[i
].name
); 
 175         tmp
.type 
= CLI_HELP_COMMAND
; 
 176         tmp
.org 
= &commandHelp
[i
]; 
 177         helpEntries
[pos
++] = tmp
; 
 181 /* Output command help to stdout. */ 
 182 static void cliOutputCommandHelp(struct commandHelp 
*help
, int group
) { 
 183     printf("\r\n  \x1b[1m%s\x1b[0m \x1b[90m%s\x1b[0m\r\n", help
->name
, help
->params
); 
 184     printf("  \x1b[33msummary:\x1b[0m %s\r\n", help
->summary
); 
 185     printf("  \x1b[33msince:\x1b[0m %s\r\n", help
->since
); 
 187         printf("  \x1b[33mgroup:\x1b[0m %s\r\n", commandGroups
[help
->group
]); 
 191 /* Print generic help. */ 
 192 static void cliOutputGenericHelp() { 
 193     sds version 
= cliVersion(); 
 196         "Type: \"help @<group>\" to get a list of commands in <group>\r\n" 
 197         "      \"help <command>\" for help on <command>\r\n" 
 198         "      \"help <tab>\" to get a list of possible help topics\r\n" 
 199         "      \"quit\" to exit\r\n", 
 205 /* Output all command help, filtering by group or command name. */ 
 206 static void cliOutputHelp(int argc
, char **argv
) { 
 210     struct commandHelp 
*help
; 
 213         cliOutputGenericHelp(); 
 215     } else if (argc 
> 0 && argv
[0][0] == '@') { 
 216         len 
= sizeof(commandGroups
)/sizeof(char*); 
 217         for (i 
= 0; i 
< len
; i
++) { 
 218             if (strcasecmp(argv
[0]+1,commandGroups
[i
]) == 0) { 
 226     for (i 
= 0; i 
< helpEntriesLen
; i
++) { 
 227         entry 
= &helpEntries
[i
]; 
 228         if (entry
->type 
!= CLI_HELP_COMMAND
) continue; 
 232             /* Compare all arguments */ 
 233             if (argc 
== entry
->argc
) { 
 234                 for (j 
= 0; j 
< argc
; j
++) { 
 235                     if (strcasecmp(argv
[j
],entry
->argv
[j
]) != 0) break; 
 238                     cliOutputCommandHelp(help
,1); 
 242             if (group 
== help
->group
) { 
 243                 cliOutputCommandHelp(help
,0); 
 250 static void completionCallback(const char *buf
, linenoiseCompletions 
*lc
) { 
 257     if (strncasecmp(buf
,"help ",5) == 0) { 
 259         while (isspace(buf
[startpos
])) startpos
++; 
 260         mask 
= CLI_HELP_COMMAND 
| CLI_HELP_GROUP
; 
 262         mask 
= CLI_HELP_COMMAND
; 
 265     for (i 
= 0; i 
< helpEntriesLen
; i
++) { 
 266         if (!(helpEntries
[i
].type 
& mask
)) continue; 
 268         matchlen 
= strlen(buf
+startpos
); 
 269         if (strncasecmp(buf
+startpos
,helpEntries
[i
].full
,matchlen
) == 0) { 
 270             tmp 
= sdsnewlen(buf
,startpos
); 
 271             tmp 
= sdscat(tmp
,helpEntries
[i
].full
); 
 272             linenoiseAddCompletion(lc
,tmp
); 
 278 /*------------------------------------------------------------------------------ 
 279  * Networking / parsing 
 280  *--------------------------------------------------------------------------- */ 
 282 /* Send AUTH command to the server */ 
 283 static int cliAuth() { 
 285     if (config
.auth 
== NULL
) return REDIS_OK
; 
 287     reply 
= redisCommand(context
,"AUTH %s",config
.auth
); 
 289         freeReplyObject(reply
); 
 295 /* Send SELECT dbnum to the server */ 
 296 static int cliSelect() { 
 298     if (config
.dbnum 
== 0) return REDIS_OK
; 
 300     reply 
= redisCommand(context
,"SELECT %d",config
.dbnum
); 
 302         freeReplyObject(reply
); 
 308 /* Connect to the client. If force is not zero the connection is performed 
 309  * even if there is already a connected socket. */ 
 310 static int cliConnect(int force
) { 
 311     if (context 
== NULL 
|| force
) { 
 315         if (config
.hostsocket 
== NULL
) { 
 316             context 
= redisConnect(config
.hostip
,config
.hostport
); 
 318             context 
= redisConnectUnix(config
.hostsocket
); 
 322             fprintf(stderr
,"Could not connect to Redis at "); 
 323             if (config
.hostsocket 
== NULL
) 
 324                 fprintf(stderr
,"%s:%d: %s\n",config
.hostip
,config
.hostport
,context
->errstr
); 
 326                 fprintf(stderr
,"%s: %s\n",config
.hostsocket
,context
->errstr
); 
 332         /* Do AUTH and select the right DB. */ 
 333         if (cliAuth() != REDIS_OK
) 
 335         if (cliSelect() != REDIS_OK
) 
 341 static void cliPrintContextError() { 
 342     if (context 
== NULL
) return; 
 343     fprintf(stderr
,"Error: %s\n",context
->errstr
); 
 346 static sds 
cliFormatReplyTTY(redisReply 
*r
, char *prefix
) { 
 347     sds out 
= sdsempty(); 
 349     case REDIS_REPLY_ERROR
: 
 350         out 
= sdscatprintf(out
,"(error) %s\n", r
->str
); 
 352     case REDIS_REPLY_STATUS
: 
 353         out 
= sdscat(out
,r
->str
); 
 354         out 
= sdscat(out
,"\n"); 
 356     case REDIS_REPLY_INTEGER
: 
 357         out 
= sdscatprintf(out
,"(integer) %lld\n",r
->integer
); 
 359     case REDIS_REPLY_STRING
: 
 360         /* If you are producing output for the standard output we want 
 361         * a more interesting output with quoted characters and so forth */ 
 362         out 
= sdscatrepr(out
,r
->str
,r
->len
); 
 363         out 
= sdscat(out
,"\n"); 
 365     case REDIS_REPLY_NIL
: 
 366         out 
= sdscat(out
,"(nil)\n"); 
 368     case REDIS_REPLY_ARRAY
: 
 369         if (r
->elements 
== 0) { 
 370             out 
= sdscat(out
,"(empty list or set)\n"); 
 372             unsigned int i
, idxlen 
= 0; 
 378             /* Calculate chars needed to represent the largest index */ 
 385             /* Prefix for nested multi bulks should grow with idxlen+2 spaces */ 
 386             memset(_prefixlen
,' ',idxlen
+2); 
 387             _prefixlen
[idxlen
+2] = '\0'; 
 388             _prefix 
= sdscat(sdsnew(prefix
),_prefixlen
); 
 390             /* Setup prefix format for every entry */ 
 391             snprintf(_prefixfmt
,sizeof(_prefixfmt
),"%%s%%%dd) ",idxlen
); 
 393             for (i 
= 0; i 
< r
->elements
; i
++) { 
 394                 /* Don't use the prefix for the first element, as the parent 
 395                  * caller already prepended the index number. */ 
 396                 out 
= sdscatprintf(out
,_prefixfmt
,i 
== 0 ? "" : prefix
,i
+1); 
 398                 /* Format the multi bulk entry */ 
 399                 tmp 
= cliFormatReplyTTY(r
->element
[i
],_prefix
); 
 400                 out 
= sdscatlen(out
,tmp
,sdslen(tmp
)); 
 407         fprintf(stderr
,"Unknown reply type: %d\n", r
->type
); 
 413 static sds 
cliFormatReplyRaw(redisReply 
*r
) { 
 414     sds out 
= sdsempty(), tmp
; 
 418     case REDIS_REPLY_NIL
: 
 421     case REDIS_REPLY_ERROR
: 
 422         out 
= sdscatlen(out
,r
->str
,r
->len
); 
 423         out 
= sdscatlen(out
,"\n",1); 
 425     case REDIS_REPLY_STATUS
: 
 426     case REDIS_REPLY_STRING
: 
 427         out 
= sdscatlen(out
,r
->str
,r
->len
); 
 429     case REDIS_REPLY_INTEGER
: 
 430         out 
= sdscatprintf(out
,"%lld",r
->integer
); 
 432     case REDIS_REPLY_ARRAY
: 
 433         for (i 
= 0; i 
< r
->elements
; i
++) { 
 434             if (i 
> 0) out 
= sdscat(out
,config
.mb_delim
); 
 435             tmp 
= cliFormatReplyRaw(r
->element
[i
]); 
 436             out 
= sdscatlen(out
,tmp
,sdslen(tmp
)); 
 441         fprintf(stderr
,"Unknown reply type: %d\n", r
->type
); 
 447 static sds 
cliFormatReplyCSV(redisReply 
*r
) { 
 450     sds out 
= sdsempty(); 
 452     case REDIS_REPLY_ERROR
: 
 453         out 
= sdscat(out
,"ERROR,"); 
 454         out 
= sdscatrepr(out
,r
->str
,strlen(r
->str
)); 
 456     case REDIS_REPLY_STATUS
: 
 457         out 
= sdscatrepr(out
,r
->str
,r
->len
); 
 459     case REDIS_REPLY_INTEGER
: 
 460         out 
= sdscatprintf(out
,"%lld",r
->integer
); 
 462     case REDIS_REPLY_STRING
: 
 463         out 
= sdscatrepr(out
,r
->str
,r
->len
); 
 465     case REDIS_REPLY_NIL
: 
 466         out 
= sdscat(out
,"NIL\n"); 
 468     case REDIS_REPLY_ARRAY
: 
 469         for (i 
= 0; i 
< r
->elements
; i
++) { 
 470             sds tmp 
= cliFormatReplyCSV(r
->element
[i
]); 
 471             out 
= sdscatlen(out
,tmp
,sdslen(tmp
)); 
 472             if (i 
!= r
->elements
-1) out 
= sdscat(out
,","); 
 477         fprintf(stderr
,"Unknown reply type: %d\n", r
->type
); 
 483 static int cliReadReply(int output_raw_strings
) { 
 489     if (redisGetReply(context
,&_reply
) != REDIS_OK
) { 
 492         if (config
.interactive
) { 
 493             /* Filter cases where we should reconnect */ 
 494             if (context
->err 
== REDIS_ERR_IO 
&& errno 
== ECONNRESET
) 
 496             if (context
->err 
== REDIS_ERR_EOF
) 
 499         cliPrintContextError(); 
 501         return REDIS_ERR
; /* avoid compiler warning */ 
 504     reply 
= (redisReply
*)_reply
; 
 506     /* Check if we need to connect to a different node and reissue the 
 508     if (config
.cluster_mode 
&& reply
->type 
== REDIS_REPLY_ERROR 
&& 
 509         (!strncmp(reply
->str
,"MOVED",5) || !strcmp(reply
->str
,"ASK"))) 
 511         char *p 
= reply
->str
, *s
; 
 515         /* Comments show the position of the pointer as: 
 517          * [S] for pointer 's' 
 518          * [P] for pointer 'p' 
 520         s 
= strchr(p
,' ');      /* MOVED[S]3999 127.0.0.1:6381 */ 
 521         p 
= strchr(s
+1,' ');    /* MOVED[S]3999[P]127.0.0.1:6381 */ 
 524         s 
= strchr(p
+1,':');    /* MOVED 3999[P]127.0.0.1[S]6381 */ 
 526         sdsfree(config
.hostip
); 
 527         config
.hostip 
= sdsnew(p
+1); 
 528         config
.hostport 
= atoi(s
+1); 
 529         if (config
.interactive
) 
 530             printf("-> Redirected to slot [%d] located at %s:%d\n", 
 531                 slot
, config
.hostip
, config
.hostport
); 
 532         config
.cluster_reissue_command 
= 1; 
 536         if (output_raw_strings
) { 
 537             out 
= cliFormatReplyRaw(reply
); 
 539             if (config
.output 
== OUTPUT_RAW
) { 
 540                 out 
= cliFormatReplyRaw(reply
); 
 541                 out 
= sdscat(out
,"\n"); 
 542             } else if (config
.output 
== OUTPUT_STANDARD
) { 
 543                 out 
= cliFormatReplyTTY(reply
,""); 
 544             } else if (config
.output 
== OUTPUT_CSV
) { 
 545                 out 
= cliFormatReplyCSV(reply
); 
 546                 out 
= sdscat(out
,"\n"); 
 549         fwrite(out
,sdslen(out
),1,stdout
); 
 552     freeReplyObject(reply
); 
 556 static int cliSendCommand(int argc
, char **argv
, int repeat
) { 
 557     char *command 
= argv
[0]; 
 561     if (!strcasecmp(command
,"help") || !strcasecmp(command
,"?")) { 
 562         cliOutputHelp(--argc
, ++argv
); 
 566     if (context 
== NULL
) return REDIS_ERR
; 
 569     if (!strcasecmp(command
,"info") || 
 570         (argc 
== 2 && !strcasecmp(command
,"cluster") && 
 571                       (!strcasecmp(argv
[1],"nodes") || 
 572                        !strcasecmp(argv
[1],"info"))) || 
 573         (argc 
== 2 && !strcasecmp(command
,"client") && 
 574                        !strcasecmp(argv
[1],"list"))) 
 580     if (!strcasecmp(command
,"shutdown")) config
.shutdown 
= 1; 
 581     if (!strcasecmp(command
,"monitor")) config
.monitor_mode 
= 1; 
 582     if (!strcasecmp(command
,"subscribe") || 
 583         !strcasecmp(command
,"psubscribe")) config
.pubsub_mode 
= 1; 
 585     /* Setup argument length */ 
 586     argvlen 
= malloc(argc
*sizeof(size_t)); 
 587     for (j 
= 0; j 
< argc
; j
++) 
 588         argvlen
[j
] = sdslen(argv
[j
]); 
 591         redisAppendCommandArgv(context
,argc
,(const char**)argv
,argvlen
); 
 592         while (config
.monitor_mode
) { 
 593             if (cliReadReply(output_raw
) != REDIS_OK
) exit(1); 
 597         if (config
.pubsub_mode
) { 
 598             if (config
.output 
!= OUTPUT_RAW
) 
 599                 printf("Reading messages... (press Ctrl-C to quit)\n"); 
 601                 if (cliReadReply(output_raw
) != REDIS_OK
) exit(1); 
 605         if (cliReadReply(output_raw
) != REDIS_OK
) { 
 609             /* Store database number when SELECT was successfully executed. */ 
 610             if (!strcasecmp(command
,"select") && argc 
== 2) { 
 611                 config
.dbnum 
= atoi(argv
[1]); 
 615         if (config
.interval
) usleep(config
.interval
); 
 616         fflush(stdout
); /* Make it grep friendly */ 
 623 /*------------------------------------------------------------------------------ 
 625  *--------------------------------------------------------------------------- */ 
 627 static int parseOptions(int argc
, char **argv
) { 
 630     for (i 
= 1; i 
< argc
; i
++) { 
 631         int lastarg 
= i
==argc
-1; 
 633         if (!strcmp(argv
[i
],"-h") && !lastarg
) { 
 634             sdsfree(config
.hostip
); 
 635             config
.hostip 
= sdsnew(argv
[++i
]); 
 636         } else if (!strcmp(argv
[i
],"-h") && lastarg
) { 
 638         } else if (!strcmp(argv
[i
],"--help")) { 
 640         } else if (!strcmp(argv
[i
],"-x")) { 
 642         } else if (!strcmp(argv
[i
],"-p") && !lastarg
) { 
 643             config
.hostport 
= atoi(argv
[++i
]); 
 644         } else if (!strcmp(argv
[i
],"-s") && !lastarg
) { 
 645             config
.hostsocket 
= argv
[++i
]; 
 646         } else if (!strcmp(argv
[i
],"-r") && !lastarg
) { 
 647             config
.repeat 
= strtoll(argv
[++i
],NULL
,10); 
 648         } else if (!strcmp(argv
[i
],"-i") && !lastarg
) { 
 649             double seconds 
= atof(argv
[++i
]); 
 650             config
.interval 
= seconds
*1000000; 
 651         } else if (!strcmp(argv
[i
],"-n") && !lastarg
) { 
 652             config
.dbnum 
= atoi(argv
[++i
]); 
 653         } else if (!strcmp(argv
[i
],"-a") && !lastarg
) { 
 654             config
.auth 
= argv
[++i
]; 
 655         } else if (!strcmp(argv
[i
],"--raw")) { 
 656             config
.output 
= OUTPUT_RAW
; 
 657         } else if (!strcmp(argv
[i
],"--csv")) { 
 658             config
.output 
= OUTPUT_CSV
; 
 659         } else if (!strcmp(argv
[i
],"--latency")) { 
 660             config
.latency_mode 
= 1; 
 661         } else if (!strcmp(argv
[i
],"--slave")) { 
 662             config
.slave_mode 
= 1; 
 663         } else if (!strcmp(argv
[i
],"--pipe")) { 
 664             config
.pipe_mode 
= 1; 
 665         } else if (!strcmp(argv
[i
],"--bigkeys")) { 
 667         } else if (!strcmp(argv
[i
],"--eval") && !lastarg
) { 
 668             config
.eval 
= argv
[++i
]; 
 669         } else if (!strcmp(argv
[i
],"-c")) { 
 670             config
.cluster_mode 
= 1; 
 671         } else if (!strcmp(argv
[i
],"-d") && !lastarg
) { 
 672             sdsfree(config
.mb_delim
); 
 673             config
.mb_delim 
= sdsnew(argv
[++i
]); 
 674         } else if (!strcmp(argv
[i
],"-v") || !strcmp(argv
[i
], "--version")) { 
 675             sds version 
= cliVersion(); 
 676             printf("redis-cli %s\n", version
); 
 686 static sds 
readArgFromStdin(void) { 
 688     sds arg 
= sdsempty(); 
 691         int nread 
= read(fileno(stdin
),buf
,1024); 
 693         if (nread 
== 0) break; 
 694         else if (nread 
== -1) { 
 695             perror("Reading from standard input"); 
 698         arg 
= sdscatlen(arg
,buf
,nread
); 
 703 static void usage() { 
 704     sds version 
= cliVersion(); 
 708 "Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n" 
 709 "  -h <hostname>    Server hostname (default: 127.0.0.1)\n" 
 710 "  -p <port>        Server port (default: 6379)\n" 
 711 "  -s <socket>      Server socket (overrides hostname and port)\n" 
 712 "  -a <password>    Password to use when connecting to the server\n" 
 713 "  -r <repeat>      Execute specified command N times\n" 
 714 "  -i <interval>    When -r is used, waits <interval> seconds per command.\n" 
 715 "                   It is possible to specify sub-second times like -i 0.1\n" 
 716 "  -n <db>          Database number\n" 
 717 "  -x               Read last argument from STDIN\n" 
 718 "  -d <delimiter>   Multi-bulk delimiter in for raw formatting (default: \\n)\n" 
 719 "  -c               Enable cluster mode (follow -ASK and -MOVED redirections)\n" 
 720 "  --raw            Use raw formatting for replies (default when STDOUT is not a tty)\n" 
 721 "  --latency        Enter a special mode continuously sampling latency\n" 
 722 "  --slave          Simulate a slave showing commands received from the master\n" 
 723 "  --pipe           Transfer raw Redis protocol from stdin to server\n" 
 724 "  --bigkeys        Sample Redis keys looking for big keys\n" 
 725 "  --eval <file>    Send an EVAL command using the Lua script at <file>\n" 
 726 "  --help           Output this help and exit\n" 
 727 "  --version        Output version and exit\n" 
 730 "  cat /etc/passwd | redis-cli -x set mypasswd\n" 
 731 "  redis-cli get mypasswd\n" 
 732 "  redis-cli -r 100 lpush mylist x\n" 
 733 "  redis-cli -r 100 -i 1 info | grep used_memory_human:\n" 
 734 "  redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3\n" 
 735 "  (Note: when using --eval the comma separates KEYS[] from ARGV[] items)\n" 
 737 "When no command is given, redis-cli starts in interactive mode.\n" 
 738 "Type \"help\" in interactive mode for information on available commands.\n" 
 745 /* Turn the plain C strings into Sds strings */ 
 746 static char **convertToSds(int count
, char** args
) { 
 748   char **sds 
= zmalloc(sizeof(char*)*count
); 
 750   for(j 
= 0; j 
< count
; j
++) 
 751     sds
[j
] = sdsnew(args
[j
]); 
 756 #define LINE_BUFLEN 4096 
 758     sds historyfile 
= NULL
; 
 764     config
.interactive 
= 1; 
 765     linenoiseSetCompletionCallback(completionCallback
); 
 767     /* Only use history when stdin is a tty. */ 
 768     if (isatty(fileno(stdin
))) { 
 771         if (getenv("HOME") != NULL
) { 
 772             historyfile 
= sdscatprintf(sdsempty(),"%s/.rediscli_history",getenv("HOME")); 
 773             linenoiseHistoryLoad(historyfile
); 
 778     while((line 
= linenoise(context 
? config
.prompt 
: "not connected> ")) != NULL
) { 
 779         if (line
[0] != '\0') { 
 780             argv 
= sdssplitargs(line
,&argc
); 
 781             if (history
) linenoiseHistoryAdd(line
); 
 782             if (historyfile
) linenoiseHistorySave(historyfile
); 
 785                 printf("Invalid argument(s)\n"); 
 788             } else if (argc 
> 0) { 
 789                 if (strcasecmp(argv
[0],"quit") == 0 || 
 790                     strcasecmp(argv
[0],"exit") == 0) 
 793                 } else if (argc 
== 3 && !strcasecmp(argv
[0],"connect")) { 
 794                     sdsfree(config
.hostip
); 
 795                     config
.hostip 
= sdsnew(argv
[1]); 
 796                     config
.hostport 
= atoi(argv
[2]); 
 798                 } else if (argc 
== 1 && !strcasecmp(argv
[0],"clear")) { 
 799                     linenoiseClearScreen(); 
 801                     long long start_time 
= mstime(), elapsed
; 
 802                     int repeat
, skipargs 
= 0; 
 804                     repeat 
= atoi(argv
[0]); 
 805                     if (argc 
> 1 && repeat
) { 
 812                         config
.cluster_reissue_command 
= 0; 
 813                         if (cliSendCommand(argc
-skipargs
,argv
+skipargs
,repeat
) 
 818                             /* If we still cannot send the command print error. 
 819                              * We'll try to reconnect the next time. */ 
 820                             if (cliSendCommand(argc
-skipargs
,argv
+skipargs
,repeat
) 
 822                                 cliPrintContextError(); 
 824                         /* Issue the command again if we got redirected in cluster mode */ 
 825                         if (config
.cluster_mode 
&& config
.cluster_reissue_command
) { 
 831                     elapsed 
= mstime()-start_time
; 
 832                     if (elapsed 
>= 500) { 
 833                         printf("(%.2fs)\n",(double)elapsed
/1000); 
 837             /* Free the argument vector */ 
 838             while(argc
--) sdsfree(argv
[argc
]); 
 841         /* linenoise() returns malloc-ed lines like readline() */ 
 847 static int noninteractive(int argc
, char **argv
) { 
 849     if (config
.stdinarg
) { 
 850         argv 
= zrealloc(argv
, (argc
+1)*sizeof(char*)); 
 851         argv
[argc
] = readArgFromStdin(); 
 852         retval 
= cliSendCommand(argc
+1, argv
, config
.repeat
); 
 854         /* stdin is probably a tty, can be tested with S_ISCHR(s.st_mode) */ 
 855         retval 
= cliSendCommand(argc
, argv
, config
.repeat
); 
 860 static int evalMode(int argc
, char **argv
) { 
 861     sds script 
= sdsempty(); 
 866     int j
, got_comma 
= 0, keys 
= 0; 
 868     /* Load the script from the file, as an sds string. */ 
 869     fp 
= fopen(config
.eval
,"r"); 
 872             "Can't open file '%s': %s\n", config
.eval
, strerror(errno
)); 
 875     while((nread 
= fread(buf
,1,sizeof(buf
),fp
)) != 0) { 
 876         script 
= sdscatlen(script
,buf
,nread
); 
 880     /* Create our argument vector */ 
 881     argv2 
= zmalloc(sizeof(sds
)*(argc
+3)); 
 882     argv2
[0] = sdsnew("EVAL"); 
 884     for (j 
= 0; j 
< argc
; j
++) { 
 885         if (!got_comma 
&& argv
[j
][0] == ',' && argv
[j
][1] == 0) { 
 889         argv2
[j
+3-got_comma
] = sdsnew(argv
[j
]); 
 890         if (!got_comma
) keys
++; 
 892     argv2
[2] = sdscatprintf(sdsempty(),"%d",keys
); 
 895     return cliSendCommand(argc
+3-got_comma
, argv2
, config
.repeat
); 
 898 static void latencyMode(void) { 
 900     long long start
, latency
, min 
= 0, max 
= 0, tot 
= 0, count 
= 0; 
 903     if (!context
) exit(1); 
 906         reply 
= redisCommand(context
,"PING"); 
 908             fprintf(stderr
,"\nI/O error\n"); 
 911         latency 
= mstime()-start
; 
 912         freeReplyObject(reply
); 
 915             min 
= max 
= tot 
= latency
; 
 916             avg 
= (double) latency
; 
 918             if (latency 
< min
) min 
= latency
; 
 919             if (latency 
> max
) max 
= latency
; 
 921             avg 
= (double) tot
/count
; 
 923         printf("\x1b[0G\x1b[2Kmin: %lld, max: %lld, avg: %.2f (%lld samples)", 
 924             min
, max
, avg
, count
); 
 930 static void slaveMode(void) { 
 931     /* To start we need to send the SYNC command and return the payload. 
 932      * The hiredis client lib does not understand this part of the protocol 
 933      * and we don't want to mess with its buffers, so everything is performed 
 934      * using direct low-level I/O. */ 
 935     int fd 
= context
->fd
; 
 938     unsigned long long payload
; 
 940     /* Send the SYNC command. */ 
 941     if (write(fd
,"SYNC\r\n",6) != 6) { 
 942         fprintf(stderr
,"Error writing to master\n"); 
 946     /* Read $<payload>\r\n, making sure to read just up to "\n" */ 
 949         nread 
= read(fd
,p
,1); 
 951             fprintf(stderr
,"Error reading bulk length while SYNCing\n"); 
 954         if (*p 
== '\n') break; 
 958     payload 
= strtoull(buf
+1,NULL
,10); 
 959     fprintf(stderr
,"SYNC with master, discarding %lld bytes of bulk tranfer...\n", 
 962     /* Discard the payload. */ 
 964         nread 
= read(fd
,buf
,(payload 
> sizeof(buf
)) ? sizeof(buf
) : payload
); 
 966             fprintf(stderr
,"Error reading RDB payload while SYNCing\n"); 
 971     fprintf(stderr
,"SYNC done. Logging commands from master.\n"); 
 973     /* Now we can use the hiredis to read the incoming protocol. */ 
 974     config
.output 
= OUTPUT_CSV
; 
 975     while (cliReadReply(0) == REDIS_OK
); 
 978 static void pipeMode(void) { 
 979     int fd 
= context
->fd
; 
 980     long long errors 
= 0, replies 
= 0, obuf_len 
= 0, obuf_pos 
= 0; 
 981     char ibuf
[1024*16], obuf
[1024*16]; /* Input and output buffers */ 
 982     char aneterr
[ANET_ERR_LEN
]; 
 983     redisReader 
*reader 
= redisReaderCreate(); 
 985     int eof 
= 0; /* True once we consumed all the standard input. */ 
 987     char magic
[20]; /* Special reply we recognize. */ 
 991     /* Use non blocking I/O. */ 
 992     if (anetNonBlock(aneterr
,fd
) == ANET_ERR
) { 
 993         fprintf(stderr
, "Can't set the socket in non blocking mode: %s\n", 
 998     /* Transfer raw protocol and read replies from the server at the same 
1001         int mask 
= AE_READABLE
; 
1003         if (!eof 
|| obuf_len 
!= 0) mask 
|= AE_WRITABLE
; 
1004         mask 
= aeWait(fd
,mask
,1000); 
1006         /* Handle the readable state: we can read replies from the server. */ 
1007         if (mask 
& AE_READABLE
) { 
1010             /* Read from socket and feed the hiredis reader. */ 
1012                 nread 
= read(fd
,ibuf
,sizeof(ibuf
)); 
1013                 if (nread 
== -1 && errno 
!= EAGAIN 
&& errno 
!= EINTR
) { 
1014                     fprintf(stderr
, "Error reading from the server: %s\n", 
1018                 if (nread 
> 0) redisReaderFeed(reader
,ibuf
,nread
); 
1021             /* Consume replies. */ 
1023                 if (redisReaderGetReply(reader
,(void**)&reply
) == REDIS_ERR
) { 
1024                     fprintf(stderr
, "Error reading replies from server\n"); 
1028                     if (reply
->type 
== REDIS_REPLY_ERROR
) { 
1029                         fprintf(stderr
,"%s\n", reply
->str
); 
1031                     } else if (eof 
&& reply
->type 
== REDIS_REPLY_STRING 
&& 
1033                         /* Check if this is the reply to our final ECHO  
1034                          * command. If so everything was received 
1035                          * from the server. */ 
1036                         if (memcmp(reply
->str
,magic
,20) == 0) { 
1037                             printf("Last reply received from server.\n"); 
1043                     freeReplyObject(reply
); 
1048         /* Handle the writable state: we can send protocol to the server. */ 
1049         if (mask 
& AE_WRITABLE
) { 
1051                 /* Transfer current buffer to server. */ 
1052                 if (obuf_len 
!= 0) { 
1053                     ssize_t nwritten 
= write(fd
,obuf
+obuf_pos
,obuf_len
); 
1055                     if (nwritten 
== -1) { 
1056                         if (errno 
!= EAGAIN 
&& errno 
!= EINTR
) { 
1057                             fprintf(stderr
, "Error writing to the server: %s\n", 
1064                     obuf_len 
-= nwritten
; 
1065                     obuf_pos 
+= nwritten
; 
1066                     if (obuf_len 
!= 0) break; /* Can't accept more data. */ 
1068                 /* If buffer is empty, load from stdin. */ 
1069                 if (obuf_len 
== 0 && !eof
) { 
1070                     ssize_t nread 
= read(STDIN_FILENO
,obuf
,sizeof(obuf
)); 
1074                         "*2\r\n$4\r\nECHO\r\n$20\r\n01234567890123456789\r\n"; 
1078                         /* Everything transfered, so we queue a special 
1079                          * ECHO command that we can match in the replies 
1080                          * to make sure everything was read from the server. */ 
1081                         for (j 
= 0; j 
< 20; j
++) 
1082                             magic
[j
] = rand() & 0xff; 
1083                         memcpy(echo
+19,magic
,20); 
1084                         memcpy(obuf
,echo
,sizeof(echo
)-1); 
1085                         obuf_len 
= sizeof(echo
)-1; 
1087                         printf("All data transferred. Waiting for the last reply...\n"); 
1088                     } else if (nread 
== -1) { 
1089                         fprintf(stderr
, "Error reading from stdin: %s\n", 
1097                 if (obuf_len 
== 0 && eof
) break; 
1101     redisReaderFree(reader
); 
1102     printf("errors: %lld, replies: %lld\n", errors
, replies
); 
1109 #define TYPE_STRING 0 
1115 static void findBigKeys(void) { 
1116     unsigned long long biggest
[5] = {0,0,0,0,0}; 
1117     unsigned long long samples 
= 0; 
1118     redisReply 
*reply1
, *reply2
, *reply3 
= NULL
; 
1119     char *sizecmd
, *typename
[] = {"string","list","set","hash","zset"}; 
1122     printf("\n# Press ctrl+c when you have had enough of it... :)\n"); 
1123     printf("# You can use -i 0.1 to sleep 0.1 sec every 100 sampled keys\n"); 
1124     printf("# in order to reduce server load (usually not needed).\n\n"); 
1126         /* Sample with RANDOMKEY */ 
1127         reply1 
= redisCommand(context
,"RANDOMKEY"); 
1128         if (reply1 
== NULL
) { 
1129             fprintf(stderr
,"\nI/O error\n"); 
1131         } else if (reply1
->type 
== REDIS_REPLY_ERROR
) { 
1132             fprintf(stderr
, "RANDOMKEY error: %s\n", 
1136         /* Get the key type */ 
1137         reply2 
= redisCommand(context
,"TYPE %s",reply1
->str
); 
1138         assert(reply2 
&& reply2
->type 
== REDIS_REPLY_STATUS
); 
1141         /* Get the key "size" */ 
1142         if (!strcmp(reply2
->str
,"string")) { 
1145         } else if (!strcmp(reply2
->str
,"list")) { 
1148         } else if (!strcmp(reply2
->str
,"set")) { 
1151         } else if (!strcmp(reply2
->str
,"hash")) { 
1154         } else if (!strcmp(reply2
->str
,"zset")) { 
1157         } else if (!strcmp(reply2
->str
,"none")) { 
1158             freeReplyObject(reply1
); 
1159             freeReplyObject(reply2
); 
1160             freeReplyObject(reply3
); 
1163             fprintf(stderr
, "Unknown key type '%s' for key '%s'\n", 
1164                 reply2
->str
, reply1
->str
); 
1168         reply3 
= redisCommand(context
,"%s %s", sizecmd
, reply1
->str
); 
1169         if (reply3 
&& reply3
->type 
== REDIS_REPLY_INTEGER
) { 
1170             if (biggest
[type
] < reply3
->integer
) { 
1171                 printf("[%6s] %s | biggest so far with size %llu\n", 
1172                     typename
[type
], reply1
->str
, 
1173                     (unsigned long long) reply3
->integer
); 
1174                 biggest
[type
] = reply3
->integer
; 
1178         if ((samples 
% 1000000) == 0) 
1179             printf("(%llu keys sampled)\n", samples
); 
1181         if ((samples 
% 100) == 0 && config
.interval
) 
1182             usleep(config
.interval
); 
1184         freeReplyObject(reply1
); 
1185         freeReplyObject(reply2
); 
1186         if (reply3
) freeReplyObject(reply3
); 
1190 int main(int argc
, char **argv
) { 
1193     config
.hostip 
= sdsnew("127.0.0.1"); 
1194     config
.hostport 
= 6379; 
1195     config
.hostsocket 
= NULL
; 
1197     config
.interval 
= 0; 
1199     config
.interactive 
= 0; 
1200     config
.shutdown 
= 0; 
1201     config
.monitor_mode 
= 0; 
1202     config
.pubsub_mode 
= 0; 
1203     config
.latency_mode 
= 0; 
1204     config
.cluster_mode 
= 0; 
1205     config
.slave_mode 
= 0; 
1206     config
.pipe_mode 
= 0; 
1208     config
.stdinarg 
= 0; 
1211     if (!isatty(fileno(stdout
)) && (getenv("FAKETTY") == NULL
)) 
1212         config
.output 
= OUTPUT_RAW
; 
1214         config
.output 
= OUTPUT_STANDARD
; 
1215     config
.mb_delim 
= sdsnew("\n"); 
1218     firstarg 
= parseOptions(argc
,argv
); 
1223     if (config
.latency_mode
) { 
1229     if (config
.slave_mode
) { 
1235     if (config
.pipe_mode
) { 
1241     if (config
.bigkeys
) { 
1246     /* Start interactive mode when no command is provided */ 
1247     if (argc 
== 0 && !config
.eval
) { 
1248         /* Note that in repl mode we don't abort on connection error. 
1249          * A new attempt will be performed for every command send. */ 
1254     /* Otherwise, we have some arguments to execute */ 
1255     if (cliConnect(0) != REDIS_OK
) exit(1); 
1257         return evalMode(argc
,argv
); 
1259         return noninteractive(argc
,convertToSds(argc
,argv
));