挑战:用更简洁优美的方式将《程序员修炼之道》中的 Shell 命令转为 PowerShell

2021-01-09 15:36:41 +08:00
 AndyAO

在《程序员修炼之道》的“变换式编程”章节中有这样一段 Shell 命令,其作用是列出当前目录中行数最多的 5 个文件。

$ find . -type f | xargs wc -l | sort -n | tail -6 | head -5
     470 ./debug.pml
     470 ./test_to_build.pml
     487 ./dbc.pml
     719 ./domain_languages.pml
     727 ./dry.pml

我算是 PowerShell 新手,折腾了老长时间之后终于写出来了,总感觉不如 Shell 的优雅,应该还有很大的优化空间。

(Get-ChildItem .\ | ForEach-Object {$_ | Select-Object -Property 'Name', @{label = 'Lines'; expression = {($_ | Get-Content).Length}}} |Sort-Object -Property 'Lines')|Select-Object -Last 5

这里发出自己的答案,如果你有兴趣,而且有信心,能够写出更优雅简洁实现,可以来试试看。


当然不必使用别名和缩写来缩短长度,虽然看起来更简洁了,但损失了可读性。

“实话实说”好像是微软的一贯命名风格,比如说新线程在 Linux 里面用 fork,在 Windows 里面好像就是 creatProcess,感觉是微软的那个命名方法更加具有可读性,况且 PowerShell 中还能设置别名,想要多少短都行。

4564 次点击
所在节点    程序员
38 条回复
AndyAO
2021-01-10 09:58:30 +08:00
@geelaw
谢谢你给出自己的答案,学到了很多.

@autoxbc @msg7086 @FrankHB @zhuangzhuang1988 @felixcode
嗨,伙计们,我终于想出更简洁优美的方式了,感到挺激动的.

对于我来讲,这个写法已经比 Shell 有过之而无不及了!

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

dir -file | % {
$_ | sort {
($_ | cat).length
}
} | select -l 5
venster
2021-01-10 10:06:12 +08:00
@AndyAO 终于上了 alias 的大杀器了
AndyAO
2021-01-10 10:10:22 +08:00
@venster #22
我发现还有容易,这是新的,哈哈哈.

@hanxiV2EX
这次 Shell 更繁琐了,因为 PowerShell 只需要 3 个管道操作符号.
而且最后获取的还是对象,能够进行更多的可扩展的操作.

dir -file | sort {($_ | gc).length} | select -L 5
yannxia
2021-01-10 10:13:54 +08:00
很多人熟悉了 Linux 的 Shell 了,这是先入为主的观念,如果说拿来编程那还是 Powershell 那套好一点,不过考虑到敲击的长度问题。
msg7086
2021-01-10 10:16:38 +08:00
一般较为复杂的操作,还是以脚本语言为主。
Shell 命令要的是 quick and dirty and powerful 。
但是如果要长期用,一般都会选择整整齐齐地写成脚本。
这时候,就可以根据实际的使用环境,选择自己喜欢的语言了。
如果你是 Python 程序员这时候可以写个 Python 小程序,没有必要拘泥于使用某一种特定的 Shell 语言。

PowerShell 的确是太小众了,除非是很特殊的环境(例如要跑在第三方的 Windows 服务器上,并且强制要求源代码公开),否则有无数其他的方案可以用(例如用 C 或者 Go 写小程序,装脚本语言,等等)。
如果是自己用的话,还不如安装一下然后用自己熟悉的脚本语言来写代码了。
PowerShell 也很少用来开发其他项目,所以熟悉的人不多,毕竟相当于新学一门语言了。
AndyAO
2021-01-10 10:20:54 +08:00
@yannxia #24
我现在已经找到非常好的写法了,不管是从"长度",还是"操作次数",还是"最终结果的可扩展性"都优于 Shell !

之所以最初我那个命令比 Shell 繁琐,是因为我是个新手,不懂怎么样写出更好的,而那本书的作者是资深程序员.

这是最简洁优美版本,可以成功运行获取拥有最多函数的 5 个文件对象.

dir -file | sort {($_ | gc).l} | select -l 5
AndyAO
2021-01-10 10:44:46 +08:00
@msg7086 #25

赞同你的观点,这里补充几点.

PowerShell 也是脚本语言,是面向对象的,因为它是.NET 的类,和.NET 体系完全互通.

从 GitHub 的数据来看,依托于 Shell 的语言,Shell 是最流行的,其次就是 PowerShell.

考虑到之前微软的封闭战略,PowerShell 在很长一段时间内都只能用于 Windows,而这个平台中用命令行的场景很少.

但现在 PowerShell 已经可以在其他平台上使用了,而且已经完全开源,后面的发展还是很有希望的.

https://madnight.github.io/githut/#/pull_requests/2020/4

目前,大多数学 PowerShell 的人应该是.NET 或者 Windows 的系统管理员(运维人员),我学的目的主要是为了将命令行上的工作自动化.
darklowly
2021-01-10 11:05:31 +08:00
@FrankHB 标识符和正则星号来类比在偷换概念
aloxaf
2021-01-10 13:35:38 +08:00
看到这个问题我就知道肯定会引起不少争论 233 。我个人倒是挺喜欢 pwsh 的,最后的写法非常简洁,学习了

我也来提供一个 zsh 下的写法:

wc -l **/*(.) | sort -nr | sed -n '2,5p'

小胜 4 个字符
AndyAO
2021-01-10 13:47:28 +08:00
@aloxaf #29
谢谢参与,本来这一篇帖子的主题,并不是争论什么 Shell/语言好,而是想征集一下有没有更好的 PowerShell 写法,看来只要是类似的,关于比较帖子都会比较有争议的.

我有激动和急躁的毛病,最后犯了两个错误:

首先是那个 Length 应该是不能省略的,我当时的测试出了点问题;

其次就是字符统计也错了,我又重新尝试了一下,最长的就是那本书上的 Shell,一共是 58 个 字符,PowerShell 49 个,我看花眼了.

然后你给出的 zsh 是 40 个,夺冠了,也让我了解了 zsh 的出色的简洁性,Thanks♪(・ω・)ノ

![]( https://cdn.jsdelivr.net/gh/Andy-AO/GitHubPictureBed/img/20210110134710.png)
wellsc
2021-01-10 14:44:11 +08:00
反 人 类
Arnie97
2021-01-11 02:29:59 +08:00
可读性不错,虽然我从没学过 PowerShell,能看懂这段程序在做什么。从第 2 条附言到第 3 条的那个优化有个专门的术语:Point-free
no1xsyzy
2021-01-11 15:28:06 +08:00
no1xsyzy
2021-01-11 15:54:53 +08:00
话说起来,fork 跟 createProcess 不一样
fork 是起一个完全一样的进程作为自己的子进程,要完成 createProcess 类似的工作还需要 exec
或者采用 execvp,这就更不直观了

而且有时还会因为覆盖了信号处理方式导致子程序发生 bug

顺便来点 golf

# Python REPL ( iPython ), 69 bytes
import os;sorted((len([*open(f)]),str(f)) for f in os.scandir())[-5:]
AndyAO
2021-01-11 16:10:00 +08:00
@no1xsyzy #33

没必要,因为已经找到很完美的答案了,是我自己想出来的,在附言 3 上.

我对 fork 和 createProcess 并不了解,只是在别处看到了这个例子,直接拿来用.谢谢你补充相关的内容.
no1xsyzy
2021-01-11 19:49:32 +08:00
@AndyAO 投到 codegolf 你会看见一堆你听都没听说过的语言用十几个字节甚至几个字节解决这件事……
没什么意义(
FrankHB
2021-01-13 21:30:07 +08:00
@AndyAO PowerShell 作为一个传统意义上的 CLI shell,本身是做不到真正即时地渲染补全结果的,它需要借助 GUI shell (具体来说典型情况下就是终端模拟器)的功能。
而这点上,至少默认终端 /控制台的交互性不算很好。而论直观性,这方面终端模拟器也难以做出典型的 IDE 那么好的 UE 。
其实 MS 应该明确知道这点,所以才有 PowerShell ISE 。问题是多少 shell 用户会真习惯这样来代替 cmd 的……也就是多个不太常用了选择了。
补全列表在不太重量级的终端下这只能做成一种后验的反馈而没法总是及时即时更新,如果用户不选择是无视而使其有意义,就需要关心到底怎么获取列表信息,具体来说就是看到并且 parse 屏幕信息。(说实话 IDE 基本上也没好哪去,但是能用 IDE 的场合往往没那么“急”,即时性要求反而一般不高,而且好歹允许做得更动态……)
非即时反馈对轻度用户来说还可以是帮助(只要性能别出大娄子明显影响到响应性),但重度负载下就会容易出来我提过的阻碍人脑“乱序执行”的问题。这里所谓的重度用户,是日常需要高频进行某一类近似但又没法确保自动化的操作的用户。(可能有人觉得这主要是运维 /DevOps,但用惯 CLI 的传统开发者这里 APM 也可以很高。)

像学习历史记录提建议这只能说是一个帮助轻度用户习惯和适应的过程,对重度用户就不太有帮助了。特别地,不保证 100% 准确而必须让人去盯一下反馈再确认,这比起闭上眼都能键盘一把梭,差距是消不掉的。
这些问题其实是所有 CLI shell 共通的问题。PowerShell 在这里并没法有效突破限制,又没几个人会把 PowerShell 当成年轻人的第一门语言,没比竞品强太多的情况下,特地去上手就有点鸡肋。

我所知的 PowerShell 最擅长的还是它能调 .NET 运行时的功能比较方便,有效缓解 .NET 语言传统上缺乏成气候的 REPL 干杂活不方便的问题。(题外话,CLR 上的一些动态语言一直不温不火,C# 和 Java 的 REPL 出来也有好几年了,甚至 C++ 的都有很长历史……这些环境没普及可能更多是用户习惯而不是语言自身限制的原因。)
就算抛开历史习惯不提,真日用起来 PowerShell 比起 sh 是没有决定性优势的(终端模拟器都是适配 sh 的更多点)。
而只是写脚本,实际项目都不见得打得过稍微认真写的 shell 脚本——举例:安装 Flutter 的那坨 ps1 就比 sh 多写 bug 报错更加云里雾里……

说到这里确实是有点跑题,不过我倒是不觉得闲了就原主题做点头脑体操完全没意义——只不过大多数人应该没到这个程度罢。
FrankHB
2021-01-13 21:44:49 +08:00
@Arnie97 Point-free 说的是一种 style 而不是 transformation,第 3 条那个也没做到 point-free (消掉了 $testPath 这个 point,但 $_ 还是 point )。
另外,point-free 跟可读性沾不上多少边,“尽量 point-free ”在抽象能力上倒明确是一种倒退。(虽然这个上下文中不明显。)
https://github.com/FrankHB/pl-docs/blob/master/zh-CN/combinator-critique.md

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/743334

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX