V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
orannge
V2EX  ›  Windows

二进制管道, PowerShell 竟还不如 CMD

  •  
  •   orannge · 2021-01-26 21:10:04 +08:00 · 5348 次点击
    这是一个创建于 1405 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这几天用了下 PowerShell,发现用到二进制管道的地方速度奇慢,例如tar -cf - 1.bin > 1.tgz。测试文件 100M,CMD 敲下命令就出结果了,PowerShell 用了整整三十来秒!就算执行结束了,结果也还是错的。

    类似命令还有cat 1.bin > 2.bin,进一步了解之后发现catGet-Content的别名,会以字符串读取文件,> 也不是重定向流,仅仅是 | Out-File 的语法糖。找了一圈,设置编码等等,发现官方并没有等效命令,GitHub 16 年的 issue 现在都没关(PowerShell#1908)?

    有个第三方模块说可以解决这个问题,Use-RawPipeline,好的装上,于是命令变成run tar "-cf" "-" 1.bin | out2 1.tgz,虽然不那么直观,参数引号不能忘,速度差了一倍,好歹是能用了。然后这模块加载要 700ms,终端打开都要卡一下,于是设置function load { Import-Module -Name Use-RawPipeline },用到相关命令的时候手动 load 一下。啊这,cmd /c tar -cf - 1.bin > 1.tgz不香吗?

    测试用的内存盘,综合下来 WSL2 满速 900M/S,CMD 满速 700M/S,PowerShell 7.1 只有 40M/S,Use-RawPipeline 速度 320M/S 。PowerShell 15-20 倍速度损失,Use-RawPipeline 1 倍速度损失。二进制管道用到的地方还是挺多的,tar/git/curl 等等。

    选择 PowerShell 是因为想在 Windows 下用用脚本,毕竟 CMD 实在太难用了。有说 PowerShell 已经跨平台,比 bash 都好用,结果连 CMD 都完全替代不了,哈哈。

    第 1 条附言  ·  2021-01-27 19:40:11 +08:00
    PowerShell 在 Windows 下大多数情况还是挺好用的,跟 C#深度结合,够现代化。我开始以为 PowerShell 是 CMD 的替代品,就像 bash/zsh 之于 Linux,任选其一即可,不存在无法替代的情况。合适的工具干合适的事,用 PowerShell 就跟随它的设计理念吧。

    还有一点是如果开发一个全平台工具,要考虑到用户可能在 PowerShell 执行,一些用法并不通用。如果是自用脚本,不考虑 PowerShell,如楼上所说,也还有 GitSCM/Cygwin/coreutils 可供选择。
    36 条回复    2021-02-13 14:04:36 +08:00
    Jirajine
        1
    Jirajine  
       2021-01-26 21:16:32 +08:00 via Android
    慢可能是因为二进制数据先被解析成 string 了,并且对无效字符可能进行替换。你会发现最终产生出来的文件已经面目全非。
    nightwitch
        2
    nightwitch  
       2021-01-26 21:29:51 +08:00
    裸 powershell 冷启动也要几百毫秒
    稍微加点 oh-my-posh 再加点其他模块,冷启动时间慢到不能忍受.
    leido
        3
    leido  
       2021-01-26 21:32:08 +08:00
    powershell 的管道不是原始字节流 , 包装成 .net 对象 的
    codehz
        4
    codehz  
       2021-01-26 21:34:17 +08:00
    其实说到底就是 windows 的坑,0202 年还在折腾编码问题,以至于不能 utf-8 走天下。。。如果假设所有终端程序都用 utf-8,就不会做出先解析输出内容的设计决策。。。
    leido
        5
    leido  
       2021-01-26 21:36:07 +08:00
    AndyAO
        6
    AndyAO  
       2021-01-26 21:37:49 +08:00   ❤️ 3
    Out-File 处理的本来就是字符串,不是二进制数据,这个在文档上有,所以只要看过文档就应该立即能明白.

    https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/out-file?view=powershell-7.1

    没有什么东西是完美的,这是 PowerShell 取舍,他认为这么做在大多数情况下效率都会更高.

    如果你认为 PowerShell 这个判断是错误的,可以用其他方式来实现.例如,你可以直接在 PowerShell 中调用 CMD 命令.

    学习使用 PowerShell,最好是以 PowerShell 的思维来思考,而不是要求 PowerShell 迁就你之前的习惯.
    by73
        7
    by73  
       2021-01-26 21:47:32 +08:00
    跟 pwsh 的设计有点关系,pwsh 管道传递的是 object,基本所有操作都是基于 object 和字符串的,某些情况下用着会比较舒服(例如获取一些信息,可以直接用 select,不需要写正则去 grep 等),而且 pwsh 最大的一个特性是跟 csharp 的代码结合的很好,虽然我不怎么用 = =

    P.S. 我也要吐槽以下 pwsh,重定向总喜欢给我输出 UTF16 。。
    AndyAO
        8
    AndyAO  
       2021-01-26 21:48:42 +08:00
    tar/git/curl 这些东西,都是类 Unix 平台上的工具,可以说是量身打造的.

    在 PowerShell 上来使用它们,效率不如原来的平台上高几乎是必然的.

    作为后来者这个很吃亏,如果有人在 C#上写了完全兼容 Git 的工具,那么效率就会很高,其他工具也是如此.
    orannge
        9
    orannge  
    OP
       2021-01-26 21:57:12 +08:00
    @AndyAO 这么说都是生态问题,类似这种命令本就是留给 CMD 来处理的
    AndyAO
        10
    AndyAO  
       2021-01-26 22:18:24 +08:00   ❤️ 1
    @orannge #9
    1. 是否是生态的问题

    在第 8 楼上,我的意思是这样的,如果有类似的工具是用 C#写的,那么用 Unix 上的的使用方式反而是蹩脚的.

    之所以感觉很蹩脚很不好用,是因为你在跨平台使用这些工具,例如 git,在 Windows 跑起来就需要很多支持工具,有兼容性问题是难免的.

    我不确定 C#上是否已经有很好的工具能满足你那个操作需求,如果能的话,那么你可以更改自己的习惯,如果没有这样的好工具,那就是生态问题.

    这个生态问题可能会很长时间都解决不了,因为新的工具要想替代老的,除非好很多,否则的话一般是不会替代的,会将就着用,很长很长时间.

    2. 是不是本来就留给 CMD 处理的

    坦白讲,我也不清楚他们团队的想法,但让我想到了此前遇到的问题,那就是建立软硬链接,必须要调 CMD,否则就要用 Win32Api.

    你现在去 StackOverFlow 还能看到这样的问题,那些被标记为√的,都是这个方案.

    但是在 PowerShell V5 后就内置了,为 New-Item 增加了新的参数,支持所有链接.

    也许谁都想做尽善尽美的东西,能力有限,还是要分个轻重缓急的吧.
    favourstreet
        11
    favourstreet  
       2021-01-26 22:26:58 +08:00 via Android
    所以我在 powershell 里常用的一个命令是 cmd.exe /c "……"
    rekulas
        12
    rekulas  
       2021-01-26 22:31:52 +08:00
    win 平台一直用 gitscm 基本没遇到什么问题 而且跟 linux 工具包一致 通畅
    Tumblr
        13
    Tumblr  
       2021-01-26 23:00:13 +08:00
    感觉就是敲了几个命令就来妄加评判的无知之谈。。。
    通篇在说读写速率的问题,没看到哪条佐证楼主观点,甚至楼主都貌似不知道 PowerShell 的管道传输的是对象。。。

    力气不够怪刀重,功夫不精怨招轻。。。
    iamwho
        14
    iamwho  
       2021-01-26 23:03:46 +08:00
    UnknownSky
        15
    UnknownSky  
       2021-01-26 23:24:48 +08:00 via Android
    emm,关闭 WD 试试,日常遇到 Powershell 命令慢的要死,把 Windows Defender 关了就好了
    ETiV
        16
    ETiV  
       2021-01-26 23:32:53 +08:00 via iPhone
    LZ 会写 bash 脚本的话,可以装个 Cygwin 工具集合
    我既不会 Power Shell 又不想写 bat
    在 Windows 上基本都是用的 Cygwin+bash 了
    lxilu
        17
    lxilu  
       2021-01-27 00:18:04 +08:00 via iPhone
    @codehz 这应是设计为`了对象管道
    bthulu
        18
    bthulu  
       2021-01-27 08:53:46 +08:00
    @AndyAO 诺基亚也是这样想的, 绝不能迁就用户的习惯
    AndyAO
        19
    AndyAO  
       2021-01-27 09:06:26 +08:00
    @bthulu #16 苹果和特斯拉也是
    AndyAO
        20
    AndyAO  
       2021-01-27 10:19:14 +08:00
    看到前面很多人提到了 PowerShell 模块加载的速度问题.
    其实模块没有必要提前进行加载,除非直接对全局进行了更改,比如说 posh-git.
    在 PowerShell 中,只要你的模块是安装在指定目录中的,可以在首次使用的时候自动加载.
    很多人可能不清楚这点,所以就把很多的模块在最初全部导入了,那速度肯定快不了.
    不过,如果这些模块确定要使用,那么提前导入还是有好处的,加快了使用时候的速度和提高了一致性.
    ragnaroks
        21
    ragnaroks  
       2021-01-27 10:56:44 +08:00
    如果你使用 CSharp 的话,powershell 可以理解为把 CSharp 当作脚本来用

    尴尬的是 CSharp 本来就可以当脚本用,而且 VS 比 ISE 不知道高到哪里去了
    ragnaroks
        22
    ragnaroks  
       2021-01-27 10:59:01 +08:00
    powershell 的 curl 是 Invoke-WebRequest 的别名,遇到过一次 Invoke-WebRequest 不正常,后来装系统都直接把 powershell.exe 链接到 cmd.exe
    jorneyr
        23
    jorneyr  
       2021-01-27 13:31:06 +08:00
    Powershell 里执行 java -jar -Dfile.encoding=UTF-8 xx.jar 报错,CMD 里没问题,不用 -Dfile.encoding 就没事。
    AndyAO
        24
    AndyAO  
       2021-01-27 14:24:58 +08:00
    #20 #18

    现在已经很少有人会用 ISE 了,你应该是有段时间没有用 PowerShell 了.
    目前 PowerShell 已经更新到 PowerShell 7,依托于.Net Core+Visual Studio Code,实现了跨平台.ISE 已经停止更新了.

    目前新 PowerShell 的开发一直都很活跃,没有停下的迹象,开发团队的博客经常会更新,GitHub 上面的仓库也很活跃,这说明有很多人在使用它,而且微软看好它的未来.

    C#不符合'脚本语言'的定义(维基百科上的,可以说是大多数人的看法),它是静态类型的,有冗长的依法规则,执行之前要先进行编译,而且它的执行速度和 C 很接近.

    这些都说明 C#不是什么脚本语言.

    所以你的看法令人感到困惑.
    AndyAO
        25
    AndyAO  
       2021-01-27 14:29:22 +08:00
    #20 不小心点错了,刚刚的回复不是给你的.
    AndyAO
        26
    AndyAO  
       2021-01-27 14:39:24 +08:00
    那是因为你不懂 PowerShell 的语法
    后面几位的回复和楼主基本上都是相同的

    先假设 PowerShell 和之前的其他 Shell 用法是相同的
    然后按照那个方法使用,发现用不了之后就感到有点失望

    PowerShell 这是个新工具,使用它不是自然而然的,是需要一些学习成本的

    而其实这个问题去谷歌一下就马上能找到答案,可能大多数人真的不太喜欢离开自己的舒适区吧

    @jorneyr #20
    orannge
        27
    orannge  
    OP
       2021-01-27 19:38:58 +08:00
    @AndyAO PowerShell 在 Windows 下大多数情况还是挺好用的,跟 C#深度结合,够现代化。我开始以为 PowerShell 是 CMD 的替代品,就像 bash/zsh 之于 Linux,任选其一即可,不存在无法替代的情况。合适的工具干合适的事,用 PowerShell 就跟随它的设计理念吧。

    还有一点是如果开发一个全平台工具,要考虑到用户可能在 PowerShell 执行,一些用法并不通用。如果是自用脚本,不考虑 PowerShell,如楼上所说,也还有 GitSCM/Cygwin/coreutils 可供选择。
    jorneyr
        28
    jorneyr  
       2021-01-28 09:07:15 +08:00
    @AndyAO 没有专门学习过 Powershell 的语法,但是 java -jar -Dfile.encoding=UTF-8 xx.jar 这个命令在 CMD,Linux,Mac 下都没问题,在 Powershell 有问题,你说我要看啥语法?
    AndyAO
        29
    AndyAO  
       2021-01-28 09:17:05 +08:00
    @jorneyr #25
    去谷歌搜一下就明白的事情了,你非要让我给你粘贴上来,不学习就是这么可怕
    基本概念都没搞清楚
    AndyAO
        30
    AndyAO  
       2021-01-28 09:31:05 +08:00
    @orannge #23

    基本同意你的观点,不过我想补充一下 :

    你刚才说的那个场景,如果只是暂时用用,那么可以直接使用 CMD 等其他方法绕开,坏处就是体验有割裂,突然用不了 PowerShell 上的各种便利功能

    如果经常要用,想要让所有的操作都能和 Cmdlet 有类似体验,那么其实有途径封装成 Cmdlet

    比如说 PowerShell 最近就出了专门的工具来将这个封装工作更加的自动化

    [Announcing PowerShell Crescendo Preview.1 | PowerShell]( https://devblogs.microsoft.com/powershell/announcing-powershell-crescendo-preview-1/)


    关键是看谁用,如果真心的想使用 PowerShell,发现不懂的就去学,那么肯定不会将二进制数据直接传到管道里面去

    因为管道里面默认是对象,要不就是字符串,要不就是别的对象,二进制数据也是要封装在对象里面的,这个只要是学过基本概念的就熟悉

    开发跨平台 CLI 工具,在 PowerShell 遇到的问题就是 : 那些没有学过 PowerShell 基本概念的人,要求 PowerShell 和其他基于文本的 Shell,完全相同的人,使用上的兼容性会出问题,如果是经常使用 PowerShell,那些问题基本上都是可以绕过去的,不是什么大事,微软不傻,不可能把兼容性做得如此烂,以至于各种工具都用不了,如上所述,关于兼容性问题改进,一直都在持续
    AndyAO
        31
    AndyAO  
       2021-01-28 09:56:46 +08:00
    @jorneyr #25
    算了,我也不想说半截话,就把你那个问题给答完了

    你那个问题在谷歌上这么搜索,首个结果就会有答案`PowerShell "-Dfile.encoding=UTF-8"`

    powershell - Specify the default file encoding in Java - Stack Overflow
    https://stackoverflow.com/questions/33393001/specify-the-default-file-encoding-in-java

    所以说这就是个动动手指的问题啊,除非你不能翻墙,谷歌用不了,否则我对你不理解这个问题,感到有点难以置信

    除了上面说的方法,还有一种方法是加入`--%`,这个在与调用相关的文档上就有,所以说你哪怕有一点心思想学一下 PowerShell,那么你就不会犯这个错误

    那么总结下来就是

    `java -jar -Dfile.encoding=UTF-8 JavaHelloWorld.jar` 语法错误

    `java -jar "-Dfile.encoding=UTF-8" JavaHelloWorld.jar` 语法正确

    `java -jar -Dfile.encoding=UTF-8 JavaHelloWorld.jar` 语法正确

    这个你仔细观察一下从控制台的高亮上就能够看出来

    ![]( https://cdn.jsdelivr.net/gh/Andy-AO/GitHubPictureBed/img/20210128095422.png)

    我就不多说了,希望对你有点帮助
    AndyAO
        32
    AndyAO  
       2021-01-28 10:12:23 +08:00   ❤️ 1
    对于上面那个问题再补充一些

    在 PowerShell 上一切都是对象,包括传递参数这个过程,在其他文本 Shell 中,后面打的所有字都是字符串,但在 PowerShell 中后面的东西都是参数列表,本质上也是对象是需要经过解析之后再来调用的,而`-`是参数列表中参数名字的语法.

    所以错误提示为`Error: Unable to access jarfile .encoding=UTF-8`,`-Dfile`被 PowerShell 解释成参数的名字,所以就更没传递进去.

    面向对象之后是很有好处的,尤其是命令比较复杂的时候,直接可以在外面写个列表,然后直接传进去,根本就不需要折腾字符串了.

    当然,如果你有个早就写好或者从网上直接复制粘贴的巨长无比的可读线几乎没有的字符串命令,那么在上面说了最好的方法就是直接用`--%`,告诉 PowerShell 停止使用面向对象的思维来考虑问题,直接当所有东西都是字符串,那么问题就迎刃而解了

    参考资料:
    about_Command_Syntax
    about_Parsing
    jorneyr
        33
    jorneyr  
       2021-01-28 11:07:49 +08:00
    @AndyAO 你没明白,不是说 Powershell 解决不了,而是这么用大家都没问题,就你有问题,你说是谁的问题?所以不合群就放弃了啊!
    AndyAO
        34
    AndyAO  
       2021-01-28 12:31:48 +08:00
    @jorneyr #30
    原来你是这个意思.
    那我感到不再困惑了!

    还是像之前说的那样,大多数人都不愿意走出自己的舒适区,毕竟这样很累,风险很高.

    更新更好的东西往往和之前的不合群.
    终于买了个汽车,然后一拍方向盘,大喊'驾'
    结果发现汽车岿然不动
    发誓不再用汽车了,汽车真是个不合群的东西,和马和驴都不一样
    AndyAO
        35
    AndyAO  
       2021-01-31 14:34:05 +08:00
    C# 和 F# 都有了自己的 Script
    好像获得这个支持不长时间
    我刚知道啊
    所以说上面的朋友说 C#也能当做脚本用,的确是可以的
    目前我还没用上,不知道和 PowerShell 有什么区别
    但可以肯定的是 PowerShell 微软仍然是大力支持的,发展很快
    我想就算是 C#的某些语法被改变,支持类似脚本的模式了
    那么 Shell,以及依托于 Shell 的脚本语言仍然是必要的
    AndyAO
        36
    AndyAO  
       2021-02-13 14:04:36 +08:00
    还有就是 , 楼上朋友说 C# 能替代 PowerShell 的 , 所以显得比较鸡肋.

    这里补充更多的信息 , 纵然 C# 现在已经支持了 REPL , 使用起来是远远不及脚本语言的.

    因为那些琐碎的语法仍然大量的存在 , 根本没办法,好好的去专注的进行 REPL.

    所以说 PowerShell 的优势是 C# 很难替代的 , 在 JVM 平台上也有类似的东西,而且非常火 , 那就是 Groovy 和 Scala , 就更不用说在 JDK 9 中 , 甲骨文官方引入了 JShell , 从而让 Java 有类官方的 REPL.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1210 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 23:38 · PVG 07:38 · LAX 15:38 · JFK 18:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.