echo 输出的内容中包括调用的变量时为何会自动乱序?内详 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
mylovesaber
V2EX    Linux

echo 输出的内容中包括调用的变量时为何会自动乱序?内详

  •  
  •   mylovesaber 2023-02-28 01:52:16 +08:00 2952 次点击
    这是一个创建于 965 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本意是希望下载 github 文件的时候先检查下有没有达到拉取频率上限,并打印出提示信息,但输出的提示信息是乱序的,具体命令如下一行一条命令:

    githubGetRateInfo=$(curl -s -I -X POST https://api.github.com/users/octocat) postLimit=$(echo "${githubGetRateInfo}"|awk /^X-RateLimit-Limit/'{print $2}') postRemaining=$(echo "${githubGetRateInfo}"|awk /^X-RateLimit-Remaining/'{print $2}') echo "GitHub 调用速率为 ${postLimit} 次 /小时" 

    echo 输出结果预期效果:

    GitHub 调用速率为 60 次 /小时 

    实际输出:

     次 /小时 用速率为 60 

    以下是 curl 那条命令获取的信息,目的是筛选出X-RateLimit-LimitX-RateLimit-Remaining的值

    HTTP/1.1 404 Not Found Server: GitHub.com Date: Mon, 27 Feb 2023 17:37:26 GMT Content-Type: application/json; charset= Content-Length: 84 X-GitHub-Media-Type: github.v3; format=json x-github-api-version-selected: 2022-11-28 X-RateLimit-Limit: 60 X-RateLimit-Remaining: 57 X-RateLimit-Reset: 1677522152 X-RateLimit-Used: 3 X-RateLimit-Resource: core Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset Access-Control-Allow-Origin: * Strict-Transport-Security: max-age=31536000; includeSubdomains; preload X-Frame-Options: deny X-Content-Type-Options: nosniff X-XSS-Protection: 0 Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin Content-Security-Policy: default-src 'none' Vary: Accept-Encoding, Accept, X-Requested-With X-GitHub-Request-Id: xxxxxxxxxxx 

    有没有大佬知道这是什么原因导致的顺序错乱?是我疏忽了什么地方吗?

    36 条回复    2023-03-02 12:05:20 +08:00
    mylovesaber
        1
    mylovesaber  
    OP
       2023-02-28 03:34:57 +08:00
    解决了,估计 github 写 api 那老哥电脑是 windows 的,导致返回的 header 信息每一行都带上了 win 特有的结尾符号,在变量 githubGetRateInfo 后面加上: `|tr -d '\r'` 即可
    geelaw
        2
    geelaw  
       2023-02-28 04:36:53 +08:00   6
    @mylovesaber #1 不太确定您的结论是如何来的,但是据我所知使用 CR LF 标记大多数行结尾是 HTTP 规定的,而且是自古以来。
    julyclyde
        3
    julyclyde  
       2023-02-28 08:52:58 +08:00
    @geelaw HTTP 里用 crlf ,和 curl 输出 crlf 是两码事
    AoEiuV020CN
        4
    AoEiuV020CN  
       2023-02-28 09:10:31 +08:00 via Android
    这么调岂不是又多浪费了一次,header 还不好解析,
    github 有提供专用的接口获取 api 限制以及剩下的次数,json 直接 jq 还方便解析,

    https://api.github.com/rate_limit
    aloxaf
        5
    aloxaf  
       2023-02-28 09:15:13 +08:00
    无法复现,你的环境及 curl 版本是多少?
    HappyStraw
        6
    HappyStraw  
       2023-02-28 10:03:41 +08:00
    https://www.rfc-editor.org/rfc/rfc2616#section-5

    ```text
    Request = Request-Line ; Section 5.1
    *(( general-header ; Section 4.5
    | request-header ; Section 5.3
    | entity-header ) CRLF) ; Section 7.1
    CRLF
    [ message-body ]
    ```

    1. HTTP 是 CRLF 换行, 不是用不用 windows 的问题.

    2. 在 Archlinux curl 7.88.1 下, HEAD https://api.github.com/users/octocat, 返回为 http/2, header 为小写

    ```text
    HTTP/2 404
    server: GitHub.com
    date: Tue, 28 Feb 2023 01:45:47 GMT
    content-type: application/json; charset=
    content-length: 84
    x-github-media-type: github.v3; format=json
    x-github-api-version-selected: 2022-11-28
    x-ratelimit-limit: 60
    x-ratelimit-remaining: 52
    x-ratelimit-reset: 1677550667
    x-ratelimit-used: 8
    x-ratelimit-resource: core
    ...省略
    ```

    3. awk 设置 IGNORECASE=1 大小写不敏感, 如:

    ```bash
    echo "${githubGetRateInfo}"|awk 'BEGIN{IGNORECASE=1}/^X-RateLimit-Limit:/{print $2}'|tr -d '\r'
    ```
    hahahahahahahah
        7
    hahahahahahahah  
       2023-02-28 11:43:36 +08:00 via iPhone
    你是直接在命令行执行的还是写到 sh 里面执行的
    geelaw
        8
    geelaw  
       2023-02-28 11:47:57 +08:00
    @julyclyde #3 curl 不参与编码解析,自然是 HTTP 给什么就返回什么,否则(若是 curl 参与编码解析的话)你把 curl 传入管道的时候会出现非常糟糕的状况。
    julyclyde
        9
    julyclyde  
       2023-02-28 13:11:20 +08:00
    @geelaw 并不是的。http 一直都 crlf ,但是 linux 版本的 curl 的输出是\n 而不是\r\n
    ysc3839
        10
    ysc3839  
       2023-02-28 13:26:09 +08:00 via Android
    @julyclyde 可以自己试试,我这里输出的就是 CRLF
    $ curl -s -I -X POST https://api.github.com/users/octocat | hexdump -C
    00000000 48 54 54 50 2f 32 20 34 30 34 20 0d 0a 73 65 72 |HTTP/2 404 ..ser|
    julyclyde
        11
    julyclyde  
       2023-02-28 14:23:39 +08:00
    @ysc3839 居然真的是 0d0a ;换了 github 之外的网址也是 0d0a
    我服了

    这样的话,OP 的假设就被推翻了呢,并不是“因为 github 的人用 windows”而导致 crlf ,而是 shell 没能正确处理 crlf ?
    geelaw
        12
    geelaw  
       2023-02-28 14:30:24 +08:00
    @julyclyde #11 是你对 shell 的期待有误,Unix 风格 shell 的管道完全是二进制的,任何对数据的解读都是程序完成。

    Re: “OP 的假设就被推翻了呢”
    为什么你会觉得 OP 的假设是自然的?那明明是《我比 GitHub 做 Web API 的员工更懂 HTTP 之口嗨 Windows 用户真时髦》最新一期。
    aloxaf
        13
    aloxaf  
       2023-02-28 14:32:38 +08:00
    @aloxaf #5 啊,我搞错了,确实可以复现

    原先都没注意过这个问题,也用 shell 处理过几次 curl 输出,竟然一直没踩坑
    julyclyde
        14
    julyclyde  
       2023-02-28 15:12:24 +08:00
    @geelaw curl 既然“按文本”输出,按说应该遵照 unix 风格的习惯用\n 啊。这是我的想法。
    看起来它并没有把每一行要输出的内容“按文本”输出,而是把从 HTTP 读到的直接发给 stdout 了
    geelaw
        15
    geelaw  
       2023-02-28 15:32:09 +08:00
    @julyclyde #14 然而你的这个想法和 #3 体现的想法是矛盾的,如果你认为 curl 应该“按文本”输出,那也和 GitHub Web API 输出的是 CRLF 还是 LF 没有关系,因为此时你认为的 curl 的行为会让用户无法感知 HTTP 传输的到底是 CRLF 还是 LF 。
    julyclyde
        16
    julyclyde  
       2023-02-28 15:44:37 +08:00
    @geelaw 我确实认为和 github 输出的是“哪一种换行”没什么关系啊。
    http 用 crlf 是遵循标准
    curl 输出 crlf 在我看来简直是无法想象的事情。虽然 OP 自己说是 crlf 了,但我当时是不信的。我并没有按你推测的那么认为
    flyqie
        17
    flyqie  
       2023-02-28 15:54:15 +08:00 via Android
    @julyclyde #16

    curl 要是输出的不标准你信不信会被一堆人围着骂。。

    curl 可以跨平台,它必须尽力保证在各平台之间的统一性。

    况且,curl 从来就不是"按文本"输出,它只是原样返回数据,你觉得它按文本输出只是因为这玩意它就是文本。。

    我倒是认为 curl 就是应该返回 crlf ,这是协议指定的,curl 做的就应该是把请求原样返回到 stdout ,这没有任何疑问。
    mylovesaber
        18
    mylovesaber  
    OP
       2023-02-28 17:40:06 +08:00
    @geelaw 那肯定是我理解问题了,我不太了解这东西有说错的还请见谅
    mylovesaber
        19
    mylovesaber  
    OP
       2023-02-28 17:43:11 +08:00
    @AoEiuV020CN 我是从这看到的:
    https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limiting

    json 的话我为了解析还得再拉一个 jq ,而现在环境写这个解析判定就是为了拉一个 yq ,反而有种鸡和蛋谁先出现的辩论感觉了,不过还是感谢,我之前不知道这方式
    mylovesaber
        20
    mylovesaber  
    OP
       2023-02-28 17:45:23 +08:00
    @AoEiuV020CN 我刚刚反复网页刷新你发的这个链接,能看到剩余次数也在随着刷新的变化而变化,但几个小时不登录的话,居然可用只剩余 20 次,有点意外。。。另外对于下载 release 中文件的操作,具体指的是接口中的哪个 key 呢?下载地址: https://api.github.com/repos/mikefarah/yq/releases/latest
    mylovesaber
        21
    mylovesaber  
    OP
       2023-02-28 17:47:21 +08:00
    @aloxaf 7.81.0 和 7.29.0 都存在,前者服务器 ubuntu minimal22.04 ,后者应该是 centos7 ,
    mylovesaber
        22
    mylovesaber  
    OP
       2023-02-28 17:48:08 +08:00
    @hahahahahahahah 两个方式都尝试了,最开始脚本中出现意外输出,手动 echo 也出现了
    mylovesaber
        23
    mylovesaber  
    OP
       2023-02-28 17:55:50 +08:00
    @geelaw 是我理解的太无脑让大家见笑了,之前的确没遇到过这问题,第一次遇到给整懵了,网上查也没头绪,没想到是编码的问题
    mylovesaber
        24
    mylovesaber  
    OP
       2023-02-28 17:57:58 +08:00
    @julyclyde 哈哈,其实你也理解错我意思了,我是有点调侃甩锅的意思,但我基于假设 curl 远端显示什么就应该原封不动得打印给我的默认想法的,而不是让 curl 对输出的结果做解析后再反馈给我
    mylovesaber
        25
    mylovesaber  
    OP
       2023-02-28 18:00:09 +08:00
    关于本贴问题我昨天加班到凌晨刚准备走,最后一次测试居然发现有这个问题就两头都发了请教,那边给到了我提示,然后我把我的处理方法也补充进去了,v2 没法在回复中用 md ,也没法编辑超时的帖子,所以直接贴隔壁站的链接吧。

    https://stackoverflow.com/questions/75584213/if-the-text-output-by-linux-echo-contains-variables-the-text-will-be-out-of-ord
    AoEiuV020CN
        26
    AoEiuV020CN  
       2023-02-28 18:06:58 +08:00
    @mylovesaber #20 这个接口不会占用次数的,变了说明有其他人在用你一样的 ip ,你没有唯一的公网 ip 吧,
    建议带上 token 再调用 api ,就和 ip 无关了,

    下载地址的话就是 browser_download_url ,

    诶你既然要解析下载地址,那不还是要提前拉一个 jq 或者其他什么解析 json 的,rate limit 不就可以一样的办法解析处理, 还是没必要读 header 呀,

    官方 header 用途是你实际调用后判断是否还有剩次数,而不是在实际使用之前特地去拿 header 的,
    mylovesaber
        27
    mylovesaber  
    OP
       2023-02-28 18:42:44 +08:00
    @AoEiuV020CN 我是为了应对不同数据库的备份要求,写了个 shell 的工具,然后读取配置文件来实现获取信息,读的是 yml ,所以解析工具就需要拉一个 yq ,还对应加了一个可以检测 yq 是否有升级的模块,然后测试下载、校验等操作的代码是否工作正常的时候意外遇到了 github 有解析上限的问题,所以不得以才加了个 25 楼的模块用于检查上限的问题,再为了不占用次数的话引入个 jq 感觉是不是有点多此一举哈哈,我想了解的 key 不是那个,是你发的那个链接里面有五段,每段都有各自的 rate limit 和 remaining 信息,我不知道如果是获取 release 文件链接的话应该会占用哪个 rate limit 和 remaining 信息

    ---

    获取下载链接的话这两行就够了:
    ```shell
    remoteYQLatestHTML="$(curl -s --max-time 15 v 站不能用外链 repos/mikefarah/yq/releases/latest)"
    yqDownloadLink=$(echo "${remoteYQLatestHTML}" | grep "browser_download_url.*.yq_linux_amd64\"" | awk -F '[" ]' '{print $(NF-1)}')
    ```
    AoEiuV020CN
        28
    AoEiuV020CN  
       2023-02-28 18:58:46 +08:00 via Android
    @mylovesaber #27
    .rate.remaining 就是对应你 header 里的值,

    另外你也可以直接把链接替换成我那个,然后照样读取 header ,就不消耗次数也不用改代码了,
    AoEiuV020CN
        29
    AoEiuV020CN  
       2023-02-28 19:03:13 +08:00 via Android
    @mylovesaber #27 顺便我也遇到过同样问题,
    我最后是选择打印 http code ,api 失败就报错不继续,
    顺便加上了 token ,有 token 一小时免费一千次,没有 token 一个 ip 一小时免费只有 60 次,太小气了,
    AoEiuV020CN
        30
    AoEiuV020CN  
       2023-02-28 19:06:39 +08:00 via Android
    @mylovesaber #27 你这样就算提前判断了也还是有可能靠不住,
    可能判断时剩下一次,到真正调用时却又没了,
    毕竟天知道这个 ip 有几个人在用,
    AoEiuV020CN
        31
    AoEiuV020CN  
       2023-02-28 19:07:16 +08:00 via Android
    mylovesaber
        32
    mylovesaber  
    OP
       2023-02-28 21:28:03 +08:00
    @AoEiuV020CN 这样 ok 了,还是不减次数的舒服啊,直接筛选出来就是数字,根本不用 string 转 int

    githubGetRateInfo=$(curl -s https://api.github.com/rate_limit|xargs|grep -o "rate: {.*.}"|sed 's/,/\n/g; s/{/\n/g; s/}/\n/g; s/ \+//g')
    postLimit=$(echo "${githubGetRateInfo}" | awk -F ':' /^limit/'{print $2}')
    postRemaining=$(echo "${githubGetRateInfo}" | awk -F ':' /^remaining/'{print $2}')
    mylovesaber
        33
    mylovesaber  
    OP
       2023-02-28 21:29:50 +08:00
    @AoEiuV020CN 公司都是搭建的 gitlab ,不用 github ,所以我自己调用问题不大,不过还是感谢分享,否则之前用的那个调一次少一次
    julyclyde
        34
    julyclyde  
       2023-03-01 11:26:25 +08:00
    echo -n ${postLimit}|hexdump -C
    00000000 36 30 0d |60.|
    可以看出来 postLimit 变量里面最后含有一个 0d
    所以,为什么
    echo "GitHub 调用速率为 ${postLimit} 次 /小时"
    的结果是
    次 /小时用速率为 60
    呢?


    echo "GitHub 调用速率为 ${postLimit} 次 /小时" |hexdump -C
    00000000 47 69 74 48 75 62 20 e8 b0 83 e7 94 a8 e9 80 9f |GitHub .........|
    00000010 e7 8e 87 e4 b8 ba 20 36 30 0d 20 e6 ac a1 20 2f |...... 60. ... /|
    00000020 e5 b0 8f e6 97 b6 0a |.......|
    00000027

    看起来输出内容是对的,但是显示效果有问题
    geelaw
        35
    geelaw  
       2023-03-02 11:32:29 +08:00 via iPhone
    @julyclyde #34 CR 的作用是“回车”,也就是把光标移动到行开头,在 CR 之后继续打印,自然会踩踏先前的内容。理同 ab BKSP cd 看起来是 acd 一样。
    julyclyde
        36
    julyclyde  
       2023-03-02 12:05:20 +08:00
    @geelaw 所以问题是出在“只回车 没换行”?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     926 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 35ms UTC 18:48 PVG 02:48 LAX 11:48 JFK 14:48
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86