(转载)基于Bash命令行的百度云上传下载工具
原文链接:http://hi.baidu.com/meoow/item/aef5814bbd5be3e1bcf451e9
这是我根据百度云PCS的API写的一个基于bash的命令行工具,
使用了curl, awk, sed, xxd, bash,没有使用任何其他的语言解释器做辅助
目前,上传下载,文件列表,文件信息,创建文件夹,删除文件都没问题
复制和移动总是返回参数错误,实在找不出来哪里跟API不一致了,暂时放弃。
另外离线下载相关的API也都添加了,但是因为现在百度云不能申请,我是用的是bypy的client_id,
不知道它的离线下载相关权限没有抑或其他问题,添加任务返回500。
不过总的来说基本上对于一般使用百毒云是够用了
#!/bin/bash # Baidu Yun Command Line Interface # Depends: bash, curl, grep, awk, sed, xxd # (Thay are basicly builtin tools of any *nix system.) # Additionally, fastupload depends: head, wc, md5sum or md5, cksum # (Which are also builtin tools) # issues: # move, copy and all offline-download related commnads do not work # the latters are probably prohibited by baidu # or the current bypy client has no permission for them. #### Variables #### # This three to be changed to your own code # TOKEN='00.00000000000000000000000000000000.0000000.0000000000.000000000000000000' # baidu has prohibited registering new client id # the client_id is borrowed from bypy (Another great baidu yun cli tool written in python) CLIENT_ID=q8WE4EpCsau1oS0MplgMKNBn # you should at least login pan.baidu.com from browser once, # then you can obtain the BDUSS code in the Cookie. BDUSS='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;' ######################################## pathprefix='/apps/bypy/' ACCEPT='text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' ACCPET_LANGUAGE='zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3' ACCPET_ENCODING='identity' CACHE_CONTROL='no-cache' CONNECTION='keep-alive' DNT=1 HOST='pcs.baidu.com' ORIGIN='http://pan.baidu.com' PRAGMA='no-cache' REFERER='http://pan.baidu.com/disk/home' UA='Mozilla/5.0 (X11; Linux; rv:5.0) Gecko/5.0 Firefox/5.0' CURL_DEFAULT_ARGS=( -H "Accept: $ACCEPT" -H "Accept-Language: $ACCPET_LANGUAGE" -H "Cache-Control: $CACHE_CONTROL" -H "Connection: $CONNECTION" -H "DNT: $DNT" -H "Host: $HOST" -H "Origin: $ORIGIN" -H "Pragma: $PRAGMA" -H "Referer: $REFERER" -H "Accept-Encoding: $ACCPET_ENCODING" -A "$UA" ) #################### #### Defining functions #### # encoding character to %xx format urlencode() { local length="${#1}" local c for (( i = 0; i < length; i++ )); do c="${1:i:1}" case $c in testa-zA-Z0-9.~_-) printf "%s" "$c" ;; *) printf "%s" "$c" | xxd -p -c1 | while read x;do printf "%%%s" "$x";done esac done } # for *basic* regexp syntax surrounded by double quotes regexescape() { local len="${#1}" local c for(( i=0;i<len;i++ ));do c="${1:i:1}" case "$c" in test*\\/^$|']'|'['|'-') printf "\\\\%s" "$c" ;; *) printf "%s" "$c" esac done } # baidu yun authorize auth() { local code echo "http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=oob&scope=basic%20netdisk&" code=$(curl -Ls "http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=oob&scope=basic%20netdisk&" -H "Cookie: BDUSS=$BDUSS"|grep -o 'readonly value="[^"]*"'|grep -o '[[:xdigit:]]\{32\}') curl -Ls "https://bypy-tianze.rhcloud.com/auth?code=$code&redirect_uri=oob&"| grep -o '"[^"]\{1,\}":"[^"]\{1,\}"'|sed -n '/access_token/s/^.*:"\([[:xdigit:].-]\{1,\}\)"$/\1/p' } # print help usage() { cat <<_EOF_ $0 <command > [arguments...] Command: auth upload <FILE > [PATH] [overwrite|newcopy] fastupload <FILE > [PATH] [overwrite|newcopy] download <PATH > [SAVEDIR] [SAVENAME] list [PATH] mkdir <PATH > meta <PATH > move <FROM > <TO> copy <FROM > <TO> delete <PATH > search <PATH > <WORD> add <URI > [PATH] query <TASKID > tasks cancel <TASKID > trash retore <FILEID > _EOF_ } # JSON pretty printer tokenize () { local GREP local ESCAPE local CHAR GREP='egrep -ao' ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})' CHAR='[^[:cntrl:]"\\]' local STRING="\"$CHAR*($ESCAPE$CHAR*)*\"" local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?' local KEYWORD='null|false|true' local SPACE='[[:space:]]+' $GREP "$STRING|$NUMBER|$KEYWORD|$SPACE|." | egrep -v "^$SPACE$" } printw() { local n="$1" if test[ "$n" =~ [1-9][0-9]* ];then : else return 0 fi for((i=0;i<n;i++));do printf " " done } pprint() { local indent=0 local value=0 while read x; do case "$x" in '{'|'[') if ((value));then printf " " value=0 else printw $indent fi ((indent++)) printf "%s\n" "$x" ;; '}'|']') value=0 ((indent > 0 && indent--)) echo printw $indent printf "%s" "$x" ;; ',') value=0 echo ;; ':') value=1 printf "%s" "$x" ;; *) if ((value));then value=0 printf " " else printw $indent fi printf "%s" "$x" ;; esac done } # compact json output (parse output from pprint only) compact() { # arguments: -f -t -b -m -s # -f no fs_id # -t no ctime and mtime # -b no brackets # -m no md5 # -s no size #default is to remove double quotes local sedcmd='s/"\([^"]*\)"/\1/g;' while getopts :ftbms arg;do case "$arg" in f) sedcmd="$sedcmd"'/^ *fs_id/d;' ;; t) sedcmd="$sedcmd"'/^ *ctime/d;/^ *mtime/d;' ;; m) sedcmd="$sedcmd"'/^ *md5:/d;' ;; s) sedcmd="$sedcmd"'/^ *size:/d;' ;; b) sedcmd="$sedcmd"'/^ *{/d;s/^ *}$//;/^ *\[$/d;/^ *\]/d;s/: \[$/:/;' ;; esac done sed "$sedcmd" } autounit() { awk -v G=$((1024**3)) -v M=$((1024**2)) -v K=1024 ' { if($0~/^ *"?size"?: [[:digit:]]{1,}/){ if($2> =G){ size=sprintf("%0.1fG",$2/G) } else if($2> =M){ size=sprintf("%0.1fM",$2/M) } else if($2> =K){ size=sprintf("%0.1fK",$2/K) } else { size=$2 } sub($2,size) } print } ' } crc32() { cksum -o3 < "$1" | while read x y;do echo $x break done } if type -t md5sum > /dev/null ;then MD5='md5sum -b' else MD5='md5' fi md5() { MD5 < "$1" | while read x y;do echo "$x" break done } md5_256k() { head -c262144 "$1" | MD5 | while read x y;do echo "$x" break done } size() { wc -c "$1"| while read x y;do echo "$x" break done } ######################################## #### Parse command line arguments #### case "$1" in auth) auth exit 0 ;; u|upload) method=upload shift if test[ -z "$1" ];then echo "$0 upload <FILE > [PATH] [overwrite|newcopy]" >&2 exit 1 fi filepath="$1" filename="$(basename "$filepath")" shift path="${1:-/}/$filename" shift ondup="${1:-overwrite}" ;; f|fastupload) method=rapidupload shift if test[ -z "$1" ];then echo "$0 upload <FILE > [PATH] [overwrite|newcopy]" >&2 exit 1 fi filepath="$1" filename="$(basename "$filepath")" shift path="${1:-/}/$filename" shift ondup="${1:-overwrite}" ;; d|download) method=download shift if test[ -z "$1" ];then echo "$0 download <PATH > [SAVEDIR] [SAVENAME]" >&2 exit 1 fi path="$1" shift savedir="${1:-.}" shift savename="${1:-$(basename "$path")}" savefile="$savedir/$savename" ;; mkdir) req=POST method=mkdir shift if test[ -z "$1" ];then echo "$0 mkdir <PATH > " >&2 exit 1 fi path="$1" ;; meta) req=GET method=meta shift if test[ -z "$1" ];then echo "$0 meta <PATH > " >&2 exit 1 fi path="$1" ;; l|list) method=list shift path="${1:-/}" ;; m|move) method=move shift from="$1" shift to="$1" if test[ -z "$from" || -z "$to" ];then echo "$0 move <FROM > <TO> " >&2 exit 1 fi ;; c|copy) method=copy shift from="$1" shift to="$1" if test[ -z "$from" || -z "$to" ];then echo "$0 copy <FROM > <TO> " >&2 exit 1 fi ;; s|search) method=search shift path="$1" shift wd="$1" if test[ -z "$path" || -z "$wd" ];then echo "$0 search <PATH > <WORD> " >&2 exit 1 fi ;; del|delete) method=delete if test[ -z "$1" ];then echo "$0 delete <PATH > " >&2 exit 1 fi shift path="$1" ;; add) method=add_task shift if test[ -z "$1" ];then echo "$0 add <LINK > " >&2 exit 1 fi link="$1" shift path="$1" ;; query) method=query_task shift if test[ -z "$1" ];then echo "$0 query <TASKID > " >&2 exit 1 fi taskid="$1" ;; tasks) method=list_task ;; cancel) method=cancel_task shift if test[ -z "$1" ];then echo "$0 cancel <TASKID > " >&2 exit 1 fi taskid="$1" ;; trash) method=listrecycle ;; restore) method=restore shift if test[ -z "$1" ];then echo "$0 restore <FILEID > " >&2 exit 1 fi fsid="$1" ;; h|help) usage exit 0 ;; *) usage exit 0 ;; esac path="$pathprefix""$path" from="$pathprefix""$from" to="$pathprefix""$to" #### determining request type #### case $method in info|download|meta|list|search|generate|diff|streaming|listrecycle) req=GET ;; upload|delete|createsuperfile|move|copy|rapidupload|add_task|list_task|query_task|cancel_task|restore|mkdir) req=POST ;; esac ################################## #### determining URI ############# case $method in upload) uriprefix='https://c.pcs.baidu.com/rest/2.0/pcs/file' ;; download) uriprefix='https://d.pcs.baidu.com/rest/2.0/pcs/file' ;; list_task|query_task|cancel_task) uriprefix='https://pcs.baidu.com/rest/2.0/pcs/services/cloud_dl' ;; *) uriprefix='https://pcs.baidu.com/rest/2.0/pcs/file' ;; esac ################################## #### Building URI depends on specific method #### uriprefix="$uriprefix?access_token=$TOKEN&method=$method" case $method in upload) uri="$uriprefix&path=$(urlencode "$path")&ondup=$ondup" ;; download|mkdir|meta|delete) uri="$uriprefix&path=$(urlencode "$path")" ;; list) by=name #time name size order=asc #asc desc uri="$uriprefix&path=$(urlencode "$path")&by=$by&order=$order" ;; move|copy) uri="$uriprefix" ;; search) re=1 uri="$uriprefix&path=$(urlencode "$path")&wd=$(urlencode "$wd")&re=$re" ;; rapidupload) filelength=$(size "$filepath") filemd5=$(md5 "$filepath") fileslicemd5=$(md5_256k "$filepath") filecrc32=$(crc32 "$filepath") uri="$uriprefix&path=$(urlencode "$path")&content-length=$filelength&content-md5=$filemd5&slice-md5=$fileslicemd5&content-crc32=$filecrc32&ondup=$ondup" ;; add_task) uri="$uriprefix&save_path=$(urlencode "$path")&source_url=$(urlencode "$link")" ;; query_task) optype=1 uri="$uriprefix&task_ids=$taskid&op_type=$optype" ;; list_task) start=0 limit=20 asc=0 #0 1 task_info=1 #1 0 uri="$uriprefix&start=$start&limit=$limit&asc=$asc&need_task_info=$task_info" ;; cancel_task) uri="$uriprefix&task_id=$taskid" ;; listrecycle) uri="$uriprefix&start=0" ;; restore) uri="$uriprefix&fs_id=$fsid" ;; esac ###################################### ## Building arguments for CURL ## curlopts=( -X$req "${CURL_DEFAULT_ARGS[@]}" ) case $method in upload) curlopts=("${curlopts[@]}" -F file=@"$filepath" "$uri") ;; download) curlopts=("${curlopts[@]}" -o "$savefile" "$uri") ;; delete|add_task|list_task|query_task|cancel_task|restore|mkdir|rapidupload) curlopts=( "${curlopts[@]}" -H "Content-Length: 0" -Ls "$uri" ) ;; move|copy) curlopts=( "${curlopts[@]}" -H "Content-Type: application/json" --data param="{\"list\":[{\"from\":\"$from\",\"to\":\"$to\"}]}" -Ls "$uri" ) ;; *) curlopts=("${curlopts[@]}" -Ls "$uri") ;; esac ################################# ### Start the operation ### # echo "${curlopts[@]}"; exit case $method in download) curl "${curlopts[@]}" ;; *) curl "${curlopts[@]}"| tokenize|pprint| sed '/^ *"path": /s/^\( *"path": "\)'"$(regexescape "$pathprefix")"'\(.*"\)$/\1\2/'| compact -ftbm | autounit ;; esac ##########################
使用,假设保存成文件名bdcurl的执行文件:
如果想不用浏览器,而是直接在命令行授权给bypy来使用的话,需要在浏览器里登录 过百毒云,然后在cookie里找到BDUSS(可以用firebug,或者使用sqlite打开浏览器的数据库也能找到),把内容复制到脚本开头的 BDUSS变量里(这个有效期比较长,所以基本只复制一次就可以了,以后token过期后重新认证时可以省略这步),
bdcurl auth
等一小会就会输出token id,把这个段字符串复制到脚本开头的TOKEN变量里,然后就可以使用了。
如果不方便获取BDUSS,就需要把终端输出的网址粘贴到浏览器里获取一个code,然后把code替换
https://bypy-tianze.rhcloud.com/auth?code=$code&redirect_uri=oob&
里面$code的部分,获得最后的access_token放到脚本开始的TOKEN变量里。
然后命令行运行:
bdcurl h 看帮助
bdcurl u file1 down #上传文件到down文件夹
bdcurl f file1 down #快速上传文件(只向服务器查询校验信息,如果百度云上有相同的文件,则直接在建立该文件,不需上传,如果返回error_code,则百毒云上没有相同文件,需要使用bdcurl u上传文件)
bdcurl d down/file1 # 下载指定路径的文件
bdcurl del down/file1 # 删除指定路径的文件
bdcurl l down # 列出 down目录下的所有文件
暂时不支持批量上传下载,毕竟百毒本身没有这样的API,所谓批量得在客户端实现,但是bash来实现相对会麻烦很多,因为没有很好的解析json的方法,如果使用python,perl之类的辅助解析,那就不如干脆直接用python或perl写了。
另外输出结果使用一个compact函数对其做一定程度的简化,其简化程度可通过参数指定,脚本里默认(compact -ftbm, 倒数第四行)使用最大程度简化(如,去除花括号和方括号,去除fs_id,md5校验码,size文件大小,mtime和ctime创建/修改时间 等项目,如果传递参数里去掉-s则会保留size项),可以自行参看脚本里定义compact函数接收参数的说明,选择你合适的输出简化程度