]>
git.saurik.com Git - redis.git/blob - 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. 
  43 #define REDIS_CMD_INLINE 1 
  44 #define REDIS_CMD_BULK 2 
  45 #define REDIS_CMD_MULTIBULK 4 
  47 #define REDIS_NOTUSED(V) ((void) V) 
  49 static struct config 
{ 
  64 static struct redisCommand cmdTable
[] = { 
  65     {"auth",2,REDIS_CMD_INLINE
}, 
  66     {"get",2,REDIS_CMD_INLINE
}, 
  67     {"set",3,REDIS_CMD_BULK
}, 
  68     {"setnx",3,REDIS_CMD_BULK
}, 
  69     {"append",3,REDIS_CMD_BULK
}, 
  70     {"substr",4,REDIS_CMD_INLINE
}, 
  71     {"del",-2,REDIS_CMD_INLINE
}, 
  72     {"exists",2,REDIS_CMD_INLINE
}, 
  73     {"incr",2,REDIS_CMD_INLINE
}, 
  74     {"decr",2,REDIS_CMD_INLINE
}, 
  75     {"rpush",3,REDIS_CMD_BULK
}, 
  76     {"lpush",3,REDIS_CMD_BULK
}, 
  77     {"rpop",2,REDIS_CMD_INLINE
}, 
  78     {"lpop",2,REDIS_CMD_INLINE
}, 
  79     {"brpop",-3,REDIS_CMD_INLINE
}, 
  80     {"blpop",-3,REDIS_CMD_INLINE
}, 
  81     {"llen",2,REDIS_CMD_INLINE
}, 
  82     {"lindex",3,REDIS_CMD_INLINE
}, 
  83     {"lset",4,REDIS_CMD_BULK
}, 
  84     {"lrange",4,REDIS_CMD_INLINE
}, 
  85     {"ltrim",4,REDIS_CMD_INLINE
}, 
  86     {"lrem",4,REDIS_CMD_BULK
}, 
  87     {"rpoplpush",3,REDIS_CMD_BULK
}, 
  88     {"sadd",3,REDIS_CMD_BULK
}, 
  89     {"srem",3,REDIS_CMD_BULK
}, 
  90     {"smove",4,REDIS_CMD_BULK
}, 
  91     {"sismember",3,REDIS_CMD_BULK
}, 
  92     {"scard",2,REDIS_CMD_INLINE
}, 
  93     {"spop",2,REDIS_CMD_INLINE
}, 
  94     {"srandmember",2,REDIS_CMD_INLINE
}, 
  95     {"sinter",-2,REDIS_CMD_INLINE
}, 
  96     {"sinterstore",-3,REDIS_CMD_INLINE
}, 
  97     {"sunion",-2,REDIS_CMD_INLINE
}, 
  98     {"sunionstore",-3,REDIS_CMD_INLINE
}, 
  99     {"sdiff",-2,REDIS_CMD_INLINE
}, 
 100     {"sdiffstore",-3,REDIS_CMD_INLINE
}, 
 101     {"smembers",2,REDIS_CMD_INLINE
}, 
 102     {"zadd",4,REDIS_CMD_BULK
}, 
 103     {"zincrby",4,REDIS_CMD_BULK
}, 
 104     {"zrem",3,REDIS_CMD_BULK
}, 
 105     {"zremrangebyscore",4,REDIS_CMD_INLINE
}, 
 106     {"zmerge",-3,REDIS_CMD_INLINE
}, 
 107     {"zmergeweighed",-4,REDIS_CMD_INLINE
}, 
 108     {"zrange",-4,REDIS_CMD_INLINE
}, 
 109     {"zrank",3,REDIS_CMD_BULK
}, 
 110     {"zrevrank",3,REDIS_CMD_BULK
}, 
 111     {"zrangebyscore",-4,REDIS_CMD_INLINE
}, 
 112     {"zcount",4,REDIS_CMD_INLINE
}, 
 113     {"zrevrange",-4,REDIS_CMD_INLINE
}, 
 114     {"zcard",2,REDIS_CMD_INLINE
}, 
 115     {"zscore",3,REDIS_CMD_BULK
}, 
 116     {"incrby",3,REDIS_CMD_INLINE
}, 
 117     {"decrby",3,REDIS_CMD_INLINE
}, 
 118     {"getset",3,REDIS_CMD_BULK
}, 
 119     {"randomkey",1,REDIS_CMD_INLINE
}, 
 120     {"select",2,REDIS_CMD_INLINE
}, 
 121     {"move",3,REDIS_CMD_INLINE
}, 
 122     {"rename",3,REDIS_CMD_INLINE
}, 
 123     {"renamenx",3,REDIS_CMD_INLINE
}, 
 124     {"keys",2,REDIS_CMD_INLINE
}, 
 125     {"dbsize",1,REDIS_CMD_INLINE
}, 
 126     {"ping",1,REDIS_CMD_INLINE
}, 
 127     {"echo",2,REDIS_CMD_BULK
}, 
 128     {"save",1,REDIS_CMD_INLINE
}, 
 129     {"bgsave",1,REDIS_CMD_INLINE
}, 
 130     {"rewriteaof",1,REDIS_CMD_INLINE
}, 
 131     {"bgrewriteaof",1,REDIS_CMD_INLINE
}, 
 132     {"shutdown",1,REDIS_CMD_INLINE
}, 
 133     {"lastsave",1,REDIS_CMD_INLINE
}, 
 134     {"type",2,REDIS_CMD_INLINE
}, 
 135     {"flushdb",1,REDIS_CMD_INLINE
}, 
 136     {"flushall",1,REDIS_CMD_INLINE
}, 
 137     {"sort",-2,REDIS_CMD_INLINE
}, 
 138     {"info",1,REDIS_CMD_INLINE
}, 
 139     {"mget",-2,REDIS_CMD_INLINE
}, 
 140     {"expire",3,REDIS_CMD_INLINE
}, 
 141     {"expireat",3,REDIS_CMD_INLINE
}, 
 142     {"ttl",2,REDIS_CMD_INLINE
}, 
 143     {"slaveof",3,REDIS_CMD_INLINE
}, 
 144     {"debug",-2,REDIS_CMD_INLINE
}, 
 145     {"mset",-3,REDIS_CMD_MULTIBULK
}, 
 146     {"msetnx",-3,REDIS_CMD_MULTIBULK
}, 
 147     {"monitor",1,REDIS_CMD_INLINE
}, 
 148     {"multi",1,REDIS_CMD_INLINE
}, 
 149     {"exec",1,REDIS_CMD_INLINE
}, 
 150     {"discard",1,REDIS_CMD_INLINE
}, 
 151     {"hset",4,REDIS_CMD_MULTIBULK
}, 
 152     {"hget",3,REDIS_CMD_BULK
}, 
 153     {"hdel",3,REDIS_CMD_BULK
}, 
 154     {"hlen",2,REDIS_CMD_INLINE
}, 
 155     {"hkeys",2,REDIS_CMD_INLINE
}, 
 156     {"hvals",2,REDIS_CMD_INLINE
}, 
 157     {"hgetall",2,REDIS_CMD_INLINE
}, 
 158     {"hexists",3,REDIS_CMD_BULK
}, 
 162 static int cliReadReply(int fd
); 
 165 static struct redisCommand 
*lookupCommand(char *name
) { 
 167     while(cmdTable
[j
].name 
!= NULL
) { 
 168         if (!strcasecmp(name
,cmdTable
[j
].name
)) return &cmdTable
[j
]; 
 174 static int cliConnect(void) { 
 175     char err
[ANET_ERR_LEN
]; 
 176     static int fd 
= ANET_ERR
; 
 178     if (fd 
== ANET_ERR
) { 
 179         fd 
= anetTcpConnect(err
,config
.hostip
,config
.hostport
); 
 180         if (fd 
== ANET_ERR
) { 
 181             fprintf(stderr
, "Could not connect to Redis at %s:%d: %s", config
.hostip
, config
.hostport
, err
); 
 184         anetTcpNoDelay(NULL
,fd
); 
 189 static sds 
cliReadLine(int fd
) { 
 190     sds line 
= sdsempty(); 
 200         } else if ((ret 
== 0) || (c 
== '\n')) { 
 203             line 
= sdscatlen(line
,&c
,1); 
 206     return sdstrim(line
,"\r\n"); 
 209 static int cliReadSingleLineReply(int fd
, int quiet
) { 
 210     sds reply 
= cliReadLine(fd
); 
 212     if (reply 
== NULL
) return 1; 
 214         printf("%s\n", reply
); 
 219 static int cliReadBulkReply(int fd
) { 
 220     sds replylen 
= cliReadLine(fd
); 
 221     char *reply
, crlf
[2]; 
 224     if (replylen 
== NULL
) return 1; 
 225     bulklen 
= atoi(replylen
); 
 231     reply 
= zmalloc(bulklen
); 
 232     anetRead(fd
,reply
,bulklen
); 
 234     if (bulklen 
&& fwrite(reply
,bulklen
,1,stdout
) == 0) { 
 238     if (isatty(fileno(stdout
)) && reply
[bulklen
-1] != '\n') 
 244 static int cliReadMultiBulkReply(int fd
) { 
 245     sds replylen 
= cliReadLine(fd
); 
 248     if (replylen 
== NULL
) return 1; 
 249     elements 
= atoi(replylen
); 
 250     if (elements 
== -1) { 
 256         printf("(empty list or set)\n"); 
 260         if (cliReadReply(fd
)) return 1; 
 266 static int cliReadReply(int fd
) { 
 269     if (anetRead(fd
,&type
,1) <= 0) exit(1); 
 273         cliReadSingleLineReply(fd
,0); 
 276         return cliReadSingleLineReply(fd
,0); 
 278         printf("(integer) "); 
 279         return cliReadSingleLineReply(fd
,0); 
 281         return cliReadBulkReply(fd
); 
 283         return cliReadMultiBulkReply(fd
); 
 285         printf("protocol error, got '%c' as reply type byte\n", type
); 
 290 static int selectDb(int fd
) { 
 295     if (config
.dbnum 
== 0) 
 299     cmd 
= sdscatprintf(cmd
,"SELECT %d\r\n",config
.dbnum
); 
 300     anetWrite(fd
,cmd
,sdslen(cmd
)); 
 301     anetRead(fd
,&type
,1); 
 302     if (type 
<= 0 || type 
!= '+') return 1; 
 303     retval 
= cliReadSingleLineReply(fd
,1); 
 310 static int cliSendCommand(int argc
, char **argv
) { 
 311     struct redisCommand 
*rc 
= lookupCommand(argv
[0]); 
 312     int fd
, j
, retval 
= 0; 
 313     int read_forever 
= 0; 
 317         fprintf(stderr
,"Unknown command '%s'\n",argv
[0]); 
 321     if ((rc
->arity 
> 0 && argc 
!= rc
->arity
) || 
 322         (rc
->arity 
< 0 && argc 
< -rc
->arity
)) { 
 323             fprintf(stderr
,"Wrong number of arguments for '%s'\n",rc
->name
); 
 326     if (!strcasecmp(rc
->name
,"monitor")) read_forever 
= 1; 
 327     if ((fd 
= cliConnect()) == -1) return 1; 
 329     /* Select db number */ 
 330     retval 
= selectDb(fd
); 
 332         fprintf(stderr
,"Error setting DB num\n"); 
 336     while(config
.repeat
--) { 
 337         /* Build the command to send */ 
 339         if (rc
->flags 
& REDIS_CMD_MULTIBULK
) { 
 340             cmd 
= sdscatprintf(cmd
,"*%d\r\n",argc
); 
 341             for (j 
= 0; j 
< argc
; j
++) { 
 342                 cmd 
= sdscatprintf(cmd
,"$%lu\r\n", 
 343                     (unsigned long)sdslen(argv
[j
])); 
 344                 cmd 
= sdscatlen(cmd
,argv
[j
],sdslen(argv
[j
])); 
 345                 cmd 
= sdscatlen(cmd
,"\r\n",2); 
 348             for (j 
= 0; j 
< argc
; j
++) { 
 349                 if (j 
!= 0) cmd 
= sdscat(cmd
," "); 
 350                 if (j 
== argc
-1 && rc
->flags 
& REDIS_CMD_BULK
) { 
 351                     cmd 
= sdscatprintf(cmd
,"%lu", 
 352                         (unsigned long)sdslen(argv
[j
])); 
 354                     cmd 
= sdscatlen(cmd
,argv
[j
],sdslen(argv
[j
])); 
 357             cmd 
= sdscat(cmd
,"\r\n"); 
 358             if (rc
->flags 
& REDIS_CMD_BULK
) { 
 359                 cmd 
= sdscatlen(cmd
,argv
[argc
-1],sdslen(argv
[argc
-1])); 
 360                 cmd 
= sdscatlen(cmd
,"\r\n",2); 
 363         anetWrite(fd
,cmd
,sdslen(cmd
)); 
 366         while (read_forever
) { 
 367             cliReadSingleLineReply(fd
,0); 
 370         retval 
= cliReadReply(fd
); 
 378 static int parseOptions(int argc
, char **argv
) { 
 381     for (i 
= 1; i 
< argc
; i
++) { 
 382         int lastarg 
= i
==argc
-1; 
 384         if (!strcmp(argv
[i
],"-h") && !lastarg
) { 
 385             char *ip 
= zmalloc(32); 
 386             if (anetResolve(NULL
,argv
[i
+1],ip
) == ANET_ERR
) { 
 387                 printf("Can't resolve %s\n", argv
[i
]); 
 392         } else if (!strcmp(argv
[i
],"-h") && lastarg
) { 
 394         } else if (!strcmp(argv
[i
],"-p") && !lastarg
) { 
 395             config
.hostport 
= atoi(argv
[i
+1]); 
 397         } else if (!strcmp(argv
[i
],"-r") && !lastarg
) { 
 398             config
.repeat 
= strtoll(argv
[i
+1],NULL
,10); 
 400         } else if (!strcmp(argv
[i
],"-n") && !lastarg
) { 
 401             config
.dbnum 
= atoi(argv
[i
+1]); 
 403         } else if (!strcmp(argv
[i
],"-a") && !lastarg
) { 
 404             config
.auth 
= argv
[i
+1]; 
 406         } else if (!strcmp(argv
[i
],"-i")) { 
 407             config
.interactive 
= 1; 
 415 static sds 
readArgFromStdin(void) { 
 417     sds arg 
= sdsempty(); 
 420         int nread 
= read(fileno(stdin
),buf
,1024); 
 422         if (nread 
== 0) break; 
 423         else if (nread 
== -1) { 
 424             perror("Reading from standard input"); 
 427         arg 
= sdscatlen(arg
,buf
,nread
); 
 432 static void usage() { 
 433     fprintf(stderr
, "usage: redis-cli [-h host] [-p port] [-a authpw] [-r repeat_times] [-n db_num] [-i] cmd arg1 arg2 arg3 ... argN\n"); 
 434     fprintf(stderr
, "usage: echo \"argN\" | redis-cli [-h host] [-a authpw] [-p port] [-r repeat_times] [-n db_num] cmd arg1 arg2 ... arg(N-1)\n"); 
 435     fprintf(stderr
, "\nIf a pipe from standard input is detected this data is used as last argument.\n\n"); 
 436     fprintf(stderr
, "example: cat /etc/passwd | redis-cli set my_passwd\n"); 
 437     fprintf(stderr
, "example: redis-cli get my_passwd\n"); 
 438     fprintf(stderr
, "example: redis-cli -r 100 lpush mylist x\n"); 
 439     fprintf(stderr
, "\nRun in interactive mode: redis-cli -i or just don't pass any command\n"); 
 443 /* Turn the plain C strings into Sds strings */ 
 444 static char **convertToSds(int count
, char** args
) { 
 446   char **sds 
= zmalloc(sizeof(char*)*count
+1); 
 448   for(j 
= 0; j 
< count
; j
++) 
 449     sds
[j
] = sdsnew(args
[j
]); 
 454 static char *prompt(char *line
, int size
) { 
 459         retval 
= fgets(line
, size
, stdin
); 
 460     } while (retval 
&& *line 
== '\n'); 
 461     line
[strlen(line
) - 1] = '\0'; 
 467     int size 
= 4096, max 
= size 
>> 1, argc
; 
 470     char **ap
, *args
[max
]; 
 472     if (config
.auth 
!= NULL
) { 
 475         authargv
[0] = "AUTH"; 
 476         authargv
[1] = config
.auth
; 
 477         cliSendCommand(2, convertToSds(2, authargv
)); 
 480     while (prompt(line
, size
)) { 
 483         for (ap 
= args
; (*ap 
= strsep(&line
, " \t")) != NULL
;) { 
 485                 if (argc 
>= max
) break; 
 486                 if (strcasecmp(*ap
,"quit") == 0 || strcasecmp(*ap
,"exit") == 0) 
 494         cliSendCommand(argc
, convertToSds(argc
, args
)); 
 501 int main(int argc
, char **argv
) { 
 504     struct redisCommand 
*rc
; 
 506     config
.hostip 
= "127.0.0.1"; 
 507     config
.hostport 
= 6379; 
 510     config
.interactive 
= 0; 
 513     firstarg 
= parseOptions(argc
,argv
); 
 517     if (argc 
== 0 || config
.interactive 
== 1) repl(); 
 519     argvcopy 
= convertToSds(argc
, argv
); 
 521     /* Read the last argument from stdandard input if needed */ 
 522     if ((rc 
= lookupCommand(argv
[0])) != NULL
) { 
 523       if (rc
->arity 
> 0 && argc 
== rc
->arity
-1) { 
 524         sds lastarg 
= readArgFromStdin(); 
 525         argvcopy
[argc
] = lastarg
; 
 530     return cliSendCommand(argc
, argvcopy
);