]>
git.saurik.com Git - redis.git/blob - src/redis-cli.c
1e9f83174fb2f375daed33c38d84f39329448d1f
   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. 
  47 #include "linenoise.h" 
  50 #define REDIS_NOTUSED(V) ((void) V) 
  52 static redisContext 
*context
; 
  53 static struct config 
{ 
  66     int cluster_reissue_command
; 
  67     int stdinarg
; /* get last arg from stdin. (-x option) */ 
  69     int raw_output
; /* output mode per command */ 
  75 char *redisGitSHA1(void); 
  76 char *redisGitDirty(void); 
  78 /*------------------------------------------------------------------------------ 
  80  *--------------------------------------------------------------------------- */ 
  82 static long long mstime(void) { 
  86     gettimeofday(&tv
, NULL
); 
  87     mst 
= ((long)tv
.tv_sec
)*1000; 
  88     mst 
+= tv
.tv_usec
/1000; 
  92 static void cliRefreshPrompt(void) { 
  93     if (config
.dbnum 
== 0) 
  94         snprintf(config
.prompt
,sizeof(config
.prompt
),"redis %s:%d> ", 
  95             config
.hostip
, config
.hostport
); 
  97         snprintf(config
.prompt
,sizeof(config
.prompt
),"redis %s:%d[%d]> ", 
  98             config
.hostip
, config
.hostport
, config
.dbnum
); 
 101 /*------------------------------------------------------------------------------ 
 103  *--------------------------------------------------------------------------- */ 
 105 #define CLI_HELP_COMMAND 1 
 106 #define CLI_HELP_GROUP 2 
 114     /* Only used for help on commands */ 
 115     struct commandHelp 
*org
; 
 118 static helpEntry 
*helpEntries
; 
 119 static int helpEntriesLen
; 
 121 static sds 
cliVersion() { 
 123     version 
= sdscatprintf(sdsempty(), "%s", REDIS_VERSION
); 
 125     /* Add git commit and working tree status when available */ 
 126     if (strtoll(redisGitSHA1(),NULL
,16)) { 
 127         version 
= sdscatprintf(version
, " (git:%s", redisGitSHA1()); 
 128         if (strtoll(redisGitDirty(),NULL
,10)) 
 129             version 
= sdscatprintf(version
, "-dirty"); 
 130         version 
= sdscat(version
, ")"); 
 135 static void cliInitHelp() { 
 136     int commandslen 
= sizeof(commandHelp
)/sizeof(struct commandHelp
); 
 137     int groupslen 
= sizeof(commandGroups
)/sizeof(char*); 
 141     helpEntriesLen 
= len 
= commandslen
+groupslen
; 
 142     helpEntries 
= malloc(sizeof(helpEntry
)*len
); 
 144     for (i 
= 0; i 
< groupslen
; i
++) { 
 146         tmp
.argv 
= malloc(sizeof(sds
)); 
 147         tmp
.argv
[0] = sdscatprintf(sdsempty(),"@%s",commandGroups
[i
]); 
 148         tmp
.full 
= tmp
.argv
[0]; 
 149         tmp
.type 
= CLI_HELP_GROUP
; 
 151         helpEntries
[pos
++] = tmp
; 
 154     for (i 
= 0; i 
< commandslen
; i
++) { 
 155         tmp
.argv 
= sdssplitargs(commandHelp
[i
].name
,&tmp
.argc
); 
 156         tmp
.full 
= sdsnew(commandHelp
[i
].name
); 
 157         tmp
.type 
= CLI_HELP_COMMAND
; 
 158         tmp
.org 
= &commandHelp
[i
]; 
 159         helpEntries
[pos
++] = tmp
; 
 163 /* Output command help to stdout. */ 
 164 static void cliOutputCommandHelp(struct commandHelp 
*help
, int group
) { 
 165     printf("\r\n  \x1b[1m%s\x1b[0m \x1b[90m%s\x1b[0m\r\n", help
->name
, help
->params
); 
 166     printf("  \x1b[33msummary:\x1b[0m %s\r\n", help
->summary
); 
 167     printf("  \x1b[33msince:\x1b[0m %s\r\n", help
->since
); 
 169         printf("  \x1b[33mgroup:\x1b[0m %s\r\n", commandGroups
[help
->group
]); 
 173 /* Print generic help. */ 
 174 static void cliOutputGenericHelp() { 
 175     sds version 
= cliVersion(); 
 178         "Type: \"help @<group>\" to get a list of commands in <group>\r\n" 
 179         "      \"help <command>\" for help on <command>\r\n" 
 180         "      \"help <tab>\" to get a list of possible help topics\r\n" 
 181         "      \"quit\" to exit\r\n", 
 187 /* Output all command help, filtering by group or command name. */ 
 188 static void cliOutputHelp(int argc
, char **argv
) { 
 192     struct commandHelp 
*help
; 
 195         cliOutputGenericHelp(); 
 197     } else if (argc 
> 0 && argv
[0][0] == '@') { 
 198         len 
= sizeof(commandGroups
)/sizeof(char*); 
 199         for (i 
= 0; i 
< len
; i
++) { 
 200             if (strcasecmp(argv
[0]+1,commandGroups
[i
]) == 0) { 
 208     for (i 
= 0; i 
< helpEntriesLen
; i
++) { 
 209         entry 
= &helpEntries
[i
]; 
 210         if (entry
->type 
!= CLI_HELP_COMMAND
) continue; 
 214             /* Compare all arguments */ 
 215             if (argc 
== entry
->argc
) { 
 216                 for (j 
= 0; j 
< argc
; j
++) { 
 217                     if (strcasecmp(argv
[j
],entry
->argv
[j
]) != 0) break; 
 220                     cliOutputCommandHelp(help
,1); 
 224             if (group 
== help
->group
) { 
 225                 cliOutputCommandHelp(help
,0); 
 232 static void completionCallback(const char *buf
, linenoiseCompletions 
*lc
) { 
 239     if (strncasecmp(buf
,"help ",5) == 0) { 
 241         while (isspace(buf
[startpos
])) startpos
++; 
 242         mask 
= CLI_HELP_COMMAND 
| CLI_HELP_GROUP
; 
 244         mask 
= CLI_HELP_COMMAND
; 
 247     for (i 
= 0; i 
< helpEntriesLen
; i
++) { 
 248         if (!(helpEntries
[i
].type 
& mask
)) continue; 
 250         matchlen 
= strlen(buf
+startpos
); 
 251         if (strncasecmp(buf
+startpos
,helpEntries
[i
].full
,matchlen
) == 0) { 
 252             tmp 
= sdsnewlen(buf
,startpos
); 
 253             tmp 
= sdscat(tmp
,helpEntries
[i
].full
); 
 254             linenoiseAddCompletion(lc
,tmp
); 
 260 /*------------------------------------------------------------------------------ 
 261  * Networking / parsing 
 262  *--------------------------------------------------------------------------- */ 
 264 /* Send AUTH command to the server */ 
 265 static int cliAuth() { 
 267     if (config
.auth 
== NULL
) return REDIS_OK
; 
 269     reply 
= redisCommand(context
,"AUTH %s",config
.auth
); 
 271         freeReplyObject(reply
); 
 277 /* Send SELECT dbnum to the server */ 
 278 static int cliSelect() { 
 280     if (config
.dbnum 
== 0) return REDIS_OK
; 
 282     reply 
= redisCommand(context
,"SELECT %d",config
.dbnum
); 
 284         freeReplyObject(reply
); 
 290 /* Connect to the client. If force is not zero the connection is performed 
 291  * even if there is already a connected socket. */ 
 292 static int cliConnect(int force
) { 
 293     if (context 
== NULL 
|| force
) { 
 297         if (config
.hostsocket 
== NULL
) { 
 298             context 
= redisConnect(config
.hostip
,config
.hostport
); 
 300             context 
= redisConnectUnix(config
.hostsocket
); 
 304             fprintf(stderr
,"Could not connect to Redis at "); 
 305             if (config
.hostsocket 
== NULL
) 
 306                 fprintf(stderr
,"%s:%d: %s\n",config
.hostip
,config
.hostport
,context
->errstr
); 
 308                 fprintf(stderr
,"%s: %s\n",config
.hostsocket
,context
->errstr
); 
 314         /* Do AUTH and select the right DB. */ 
 315         if (cliAuth() != REDIS_OK
) 
 317         if (cliSelect() != REDIS_OK
) 
 323 static void cliPrintContextError() { 
 324     if (context 
== NULL
) return; 
 325     fprintf(stderr
,"Error: %s\n",context
->errstr
); 
 328 static sds 
cliFormatReplyTTY(redisReply 
*r
, char *prefix
) { 
 329     sds out 
= sdsempty(); 
 331     case REDIS_REPLY_ERROR
: 
 332         out 
= sdscatprintf(out
,"(error) %s\n", r
->str
); 
 334     case REDIS_REPLY_STATUS
: 
 335         out 
= sdscat(out
,r
->str
); 
 336         out 
= sdscat(out
,"\n"); 
 338     case REDIS_REPLY_INTEGER
: 
 339         out 
= sdscatprintf(out
,"(integer) %lld\n",r
->integer
); 
 341     case REDIS_REPLY_STRING
: 
 342         /* If you are producing output for the standard output we want 
 343         * a more interesting output with quoted characters and so forth */ 
 344         out 
= sdscatrepr(out
,r
->str
,r
->len
); 
 345         out 
= sdscat(out
,"\n"); 
 347     case REDIS_REPLY_NIL
: 
 348         out 
= sdscat(out
,"(nil)\n"); 
 350     case REDIS_REPLY_ARRAY
: 
 351         if (r
->elements 
== 0) { 
 352             out 
= sdscat(out
,"(empty list or set)\n"); 
 354             unsigned int i
, idxlen 
= 0; 
 360             /* Calculate chars needed to represent the largest index */ 
 367             /* Prefix for nested multi bulks should grow with idxlen+2 spaces */ 
 368             memset(_prefixlen
,' ',idxlen
+2); 
 369             _prefixlen
[idxlen
+2] = '\0'; 
 370             _prefix 
= sdscat(sdsnew(prefix
),_prefixlen
); 
 372             /* Setup prefix format for every entry */ 
 373             snprintf(_prefixfmt
,sizeof(_prefixfmt
),"%%s%%%dd) ",idxlen
); 
 375             for (i 
= 0; i 
< r
->elements
; i
++) { 
 376                 /* Don't use the prefix for the first element, as the parent 
 377                  * caller already prepended the index number. */ 
 378                 out 
= sdscatprintf(out
,_prefixfmt
,i 
== 0 ? "" : prefix
,i
+1); 
 380                 /* Format the multi bulk entry */ 
 381                 tmp 
= cliFormatReplyTTY(r
->element
[i
],_prefix
); 
 382                 out 
= sdscatlen(out
,tmp
,sdslen(tmp
)); 
 389         fprintf(stderr
,"Unknown reply type: %d\n", r
->type
); 
 395 static sds 
cliFormatReplyRaw(redisReply 
*r
) { 
 396     sds out 
= sdsempty(), tmp
; 
 400     case REDIS_REPLY_NIL
: 
 403     case REDIS_REPLY_ERROR
: 
 404         out 
= sdscatlen(out
,r
->str
,r
->len
); 
 405         out 
= sdscatlen(out
,"\n",1); 
 407     case REDIS_REPLY_STATUS
: 
 408     case REDIS_REPLY_STRING
: 
 409         out 
= sdscatlen(out
,r
->str
,r
->len
); 
 411     case REDIS_REPLY_INTEGER
: 
 412         out 
= sdscatprintf(out
,"%lld",r
->integer
); 
 414     case REDIS_REPLY_ARRAY
: 
 415         for (i 
= 0; i 
< r
->elements
; i
++) { 
 416             if (i 
> 0) out 
= sdscat(out
,config
.mb_delim
); 
 417             tmp 
= cliFormatReplyRaw(r
->element
[i
]); 
 418             out 
= sdscatlen(out
,tmp
,sdslen(tmp
)); 
 423         fprintf(stderr
,"Unknown reply type: %d\n", r
->type
); 
 429 static int cliReadReply(int output_raw_strings
) { 
 435     if (redisGetReply(context
,&_reply
) != REDIS_OK
) { 
 438         if (config
.interactive
) { 
 439             /* Filter cases where we should reconnect */ 
 440             if (context
->err 
== REDIS_ERR_IO 
&& errno 
== ECONNRESET
) 
 442             if (context
->err 
== REDIS_ERR_EOF
) 
 445         cliPrintContextError(); 
 447         return REDIS_ERR
; /* avoid compiler warning */ 
 450     reply 
= (redisReply
*)_reply
; 
 452     /* Check if we need to connect to a different node and reissue the request. */ 
 453     if (config
.cluster_mode 
&& reply
->type 
== REDIS_REPLY_ERROR 
&& 
 454         (!strncmp(reply
->str
,"MOVED",5) || !strcmp(reply
->str
,"ASK"))) 
 456         char *p 
= reply
->str
, *s
; 
 460         /* Comments show the position of the pointer as: 
 462          * [S] for pointer 's' 
 463          * [P] for pointer 'p' 
 465         s 
= strchr(p
,' ');      /* MOVED[S]3999 127.0.0.1:6381 */ 
 466         p 
= strchr(s
+1,' ');    /* MOVED[S]3999[P]127.0.0.1:6381 */ 
 469         s 
= strchr(p
+1,':');    /* MOVED 3999[P]127.0.0.1[S]6381 */ 
 471         sdsfree(config
.hostip
); 
 472         config
.hostip 
= sdsnew(p
+1); 
 473         config
.hostport 
= atoi(s
+1); 
 474         if (config
.interactive
) 
 475             printf("-> Redirected to slot [%d] located at %s:%d\n", 
 476                 slot
, config
.hostip
, config
.hostport
); 
 477         config
.cluster_reissue_command 
= 1; 
 481         if (output_raw_strings
) { 
 482             out 
= cliFormatReplyRaw(reply
); 
 484             if (config
.raw_output
) { 
 485                 out 
= cliFormatReplyRaw(reply
); 
 486                 out 
= sdscat(out
,"\n"); 
 488                 out 
= cliFormatReplyTTY(reply
,""); 
 491         fwrite(out
,sdslen(out
),1,stdout
); 
 494     freeReplyObject(reply
); 
 498 static int cliSendCommand(int argc
, char **argv
, int repeat
) { 
 499     char *command 
= argv
[0]; 
 503     if (context 
== NULL
) return REDIS_ERR
; 
 506     if (!strcasecmp(command
,"info") || 
 507         (argc 
== 2 && !strcasecmp(command
,"cluster") && 
 508                       (!strcasecmp(argv
[1],"nodes") || 
 509                        !strcasecmp(argv
[1],"info"))) || 
 510         (argc 
== 2 && !strcasecmp(command
,"client") && 
 511                        !strcasecmp(argv
[1],"list"))) 
 517     if (!strcasecmp(command
,"help") || !strcasecmp(command
,"?")) { 
 518         cliOutputHelp(--argc
, ++argv
); 
 521     if (!strcasecmp(command
,"shutdown")) config
.shutdown 
= 1; 
 522     if (!strcasecmp(command
,"monitor")) config
.monitor_mode 
= 1; 
 523     if (!strcasecmp(command
,"subscribe") || 
 524         !strcasecmp(command
,"psubscribe")) config
.pubsub_mode 
= 1; 
 526     /* Setup argument length */ 
 527     argvlen 
= malloc(argc
*sizeof(size_t)); 
 528     for (j 
= 0; j 
< argc
; j
++) 
 529         argvlen
[j
] = sdslen(argv
[j
]); 
 532         redisAppendCommandArgv(context
,argc
,(const char**)argv
,argvlen
); 
 533         while (config
.monitor_mode
) { 
 534             if (cliReadReply(output_raw
) != REDIS_OK
) exit(1); 
 538         if (config
.pubsub_mode
) { 
 539             if (!config
.raw_output
) 
 540                 printf("Reading messages... (press Ctrl-C to quit)\n"); 
 542                 if (cliReadReply(output_raw
) != REDIS_OK
) exit(1); 
 546         if (cliReadReply(output_raw
) != REDIS_OK
) { 
 550             /* Store database number when SELECT was successfully executed. */ 
 551             if (!strcasecmp(command
,"select") && argc 
== 2) { 
 552                 config
.dbnum 
= atoi(argv
[1]); 
 556         if (config
.interval
) usleep(config
.interval
); 
 557         fflush(stdout
); /* Make it grep friendly */ 
 564 /*------------------------------------------------------------------------------ 
 566  *--------------------------------------------------------------------------- */ 
 568 static int parseOptions(int argc
, char **argv
) { 
 571     for (i 
= 1; i 
< argc
; i
++) { 
 572         int lastarg 
= i
==argc
-1; 
 574         if (!strcmp(argv
[i
],"-h") && !lastarg
) { 
 575             sdsfree(config
.hostip
); 
 576             config
.hostip 
= sdsnew(argv
[i
+1]); 
 578         } else if (!strcmp(argv
[i
],"-h") && lastarg
) { 
 580         } else if (!strcmp(argv
[i
],"--help")) { 
 582         } else if (!strcmp(argv
[i
],"-x")) { 
 584         } else if (!strcmp(argv
[i
],"-p") && !lastarg
) { 
 585             config
.hostport 
= atoi(argv
[i
+1]); 
 587         } else if (!strcmp(argv
[i
],"-s") && !lastarg
) { 
 588             config
.hostsocket 
= argv
[i
+1]; 
 590         } else if (!strcmp(argv
[i
],"-r") && !lastarg
) { 
 591             config
.repeat 
= strtoll(argv
[i
+1],NULL
,10); 
 593         } else if (!strcmp(argv
[i
],"-i") && !lastarg
) { 
 594             double seconds 
= atof(argv
[i
+1]); 
 595             config
.interval 
= seconds
*1000000; 
 597         } else if (!strcmp(argv
[i
],"-n") && !lastarg
) { 
 598             config
.dbnum 
= atoi(argv
[i
+1]); 
 600         } else if (!strcmp(argv
[i
],"-a") && !lastarg
) { 
 601             config
.auth 
= argv
[i
+1]; 
 603         } else if (!strcmp(argv
[i
],"--raw")) { 
 604             config
.raw_output 
= 1; 
 605         } else if (!strcmp(argv
[i
],"--latency")) { 
 606             config
.latency_mode 
= 1; 
 607         } else if (!strcmp(argv
[i
],"-c")) { 
 608             config
.cluster_mode 
= 1; 
 609         } else if (!strcmp(argv
[i
],"-d") && !lastarg
) { 
 610             sdsfree(config
.mb_delim
); 
 611             config
.mb_delim 
= sdsnew(argv
[i
+1]); 
 613         } else if (!strcmp(argv
[i
],"-v") || !strcmp(argv
[i
], "--version")) { 
 614             sds version 
= cliVersion(); 
 615             printf("redis-cli %s\n", version
); 
 625 static sds 
readArgFromStdin(void) { 
 627     sds arg 
= sdsempty(); 
 630         int nread 
= read(fileno(stdin
),buf
,1024); 
 632         if (nread 
== 0) break; 
 633         else if (nread 
== -1) { 
 634             perror("Reading from standard input"); 
 637         arg 
= sdscatlen(arg
,buf
,nread
); 
 642 static void usage() { 
 643     sds version 
= cliVersion(); 
 647 "Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n" 
 648 "  -h <hostname>    Server hostname (default: 127.0.0.1)\n" 
 649 "  -p <port>        Server port (default: 6379)\n" 
 650 "  -s <socket>      Server socket (overrides hostname and port)\n" 
 651 "  -a <password>    Password to use when connecting to the server\n" 
 652 "  -r <repeat>      Execute specified command N times\n" 
 653 "  -i <interval>    When -r is used, waits <interval> seconds per command.\n" 
 654 "                   It is possible to specify sub-second times like -i 0.1.\n" 
 655 "  -n <db>          Database number\n" 
 656 "  -x               Read last argument from STDIN\n" 
 657 "  -d <delimiter>   Multi-bulk delimiter in for raw formatting (default: \\n)\n" 
 658 "  -c               Enable cluster mode (follow -ASK and -MOVED redirections)\n" 
 659 "  --raw            Use raw formatting for replies (default when STDOUT is not a tty)\n" 
 660 "  --latency        Enter a special mode continuously sampling latency.\n" 
 661 "  --help           Output this help and exit\n" 
 662 "  --version        Output version and exit\n" 
 665 "  cat /etc/passwd | redis-cli -x set mypasswd\n" 
 666 "  redis-cli get mypasswd\n" 
 667 "  redis-cli -r 100 lpush mylist x\n" 
 668 "  redis-cli -r 100 -i 1 info | grep used_memory_human:\n" 
 670 "When no command is given, redis-cli starts in interactive mode.\n" 
 671 "Type \"help\" in interactive mode for information on available commands.\n" 
 678 /* Turn the plain C strings into Sds strings */ 
 679 static char **convertToSds(int count
, char** args
) { 
 681   char **sds 
= zmalloc(sizeof(char*)*count
); 
 683   for(j 
= 0; j 
< count
; j
++) 
 684     sds
[j
] = sdsnew(args
[j
]); 
 689 #define LINE_BUFLEN 4096 
 691     sds historyfile 
= NULL
; 
 697     config
.interactive 
= 1; 
 698     linenoiseSetCompletionCallback(completionCallback
); 
 700     /* Only use history when stdin is a tty. */ 
 701     if (isatty(fileno(stdin
))) { 
 704         if (getenv("HOME") != NULL
) { 
 705             historyfile 
= sdscatprintf(sdsempty(),"%s/.rediscli_history",getenv("HOME")); 
 706             linenoiseHistoryLoad(historyfile
); 
 711     while((line 
= linenoise(context 
? config
.prompt 
: "not connected> ")) != NULL
) { 
 712         if (line
[0] != '\0') { 
 713             argv 
= sdssplitargs(line
,&argc
); 
 714             if (history
) linenoiseHistoryAdd(line
); 
 715             if (historyfile
) linenoiseHistorySave(historyfile
); 
 718                 printf("Invalid argument(s)\n"); 
 720             } else if (argc 
> 0) { 
 721                 if (strcasecmp(argv
[0],"quit") == 0 || 
 722                     strcasecmp(argv
[0],"exit") == 0) 
 725                 } else if (argc 
== 3 && !strcasecmp(argv
[0],"connect")) { 
 726                     sdsfree(config
.hostip
); 
 727                     config
.hostip 
= sdsnew(argv
[1]); 
 728                     config
.hostport 
= atoi(argv
[2]); 
 730                 } else if (argc 
== 1 && !strcasecmp(argv
[0],"clear")) { 
 731                     linenoiseClearScreen(); 
 733                     long long start_time 
= mstime(), elapsed
; 
 734                     int repeat
, skipargs 
= 0; 
 736                     repeat 
= atoi(argv
[0]); 
 737                     if (argc 
> 1 && repeat
) { 
 744                         config
.cluster_reissue_command 
= 0; 
 745                         if (cliSendCommand(argc
-skipargs
,argv
+skipargs
,repeat
) 
 750                             /* If we still cannot send the command print error. 
 751                              * We'll try to reconnect the next time. */ 
 752                             if (cliSendCommand(argc
-skipargs
,argv
+skipargs
,repeat
) 
 754                                 cliPrintContextError(); 
 756                         /* Issue the command again if we got redirected in cluster mode */ 
 757                         if (config
.cluster_mode 
&& config
.cluster_reissue_command
) { 
 763                     elapsed 
= mstime()-start_time
; 
 764                     if (elapsed 
>= 500) { 
 765                         printf("(%.2fs)\n",(double)elapsed
/1000); 
 769             /* Free the argument vector */ 
 770             while(argc
--) sdsfree(argv
[argc
]); 
 773         /* linenoise() returns malloc-ed lines like readline() */ 
 779 static int noninteractive(int argc
, char **argv
) { 
 781     if (config
.stdinarg
) { 
 782         argv 
= zrealloc(argv
, (argc
+1)*sizeof(char*)); 
 783         argv
[argc
] = readArgFromStdin(); 
 784         retval 
= cliSendCommand(argc
+1, argv
, config
.repeat
); 
 786         /* stdin is probably a tty, can be tested with S_ISCHR(s.st_mode) */ 
 787         retval 
= cliSendCommand(argc
, argv
, config
.repeat
); 
 792 static void latencyMode(void) { 
 794     long long start
, latency
, min
, max
, tot
, count 
= 0; 
 797     if (!context
) exit(1); 
 800         reply 
= redisCommand(context
,"PING"); 
 802             fprintf(stderr
,"\nI/O error\n"); 
 805         latency 
= mstime()-start
; 
 806         freeReplyObject(reply
); 
 809             min 
= max 
= tot 
= latency
; 
 810             avg 
= (double) latency
; 
 812             if (latency 
< min
) min 
= latency
; 
 813             if (latency 
> max
) max 
= latency
; 
 815             avg 
= (double) tot
/count
; 
 817         printf("\x1b[0G\x1b[2Kmin: %lld, max: %lld, avg: %.2f (%lld samples)", 
 818             min
, max
, avg
, count
); 
 824 int main(int argc
, char **argv
) { 
 827     config
.hostip 
= sdsnew("127.0.0.1"); 
 828     config
.hostport 
= 6379; 
 829     config
.hostsocket 
= NULL
; 
 833     config
.interactive 
= 0; 
 835     config
.monitor_mode 
= 0; 
 836     config
.pubsub_mode 
= 0; 
 837     config
.latency_mode 
= 0; 
 838     config
.cluster_mode 
= 0; 
 841     config
.raw_output 
= !isatty(fileno(stdout
)) && (getenv("FAKETTY") == NULL
); 
 842     config
.mb_delim 
= sdsnew("\n"); 
 845     firstarg 
= parseOptions(argc
,argv
); 
 849     /* Start in latency mode if appropriate */ 
 850     if (config
.latency_mode
) { 
 855     /* Start interactive mode when no command is provided */ 
 857         /* Note that in repl mode we don't abort on connection error. 
 858          * A new attempt will be performed for every command send. */ 
 863     /* Otherwise, we have some arguments to execute */ 
 864     if (cliConnect(0) != REDIS_OK
) exit(1); 
 865     return noninteractive(argc
,convertToSds(argc
,argv
));