惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

TaoSecurity Blog
TaoSecurity Blog
博客园 - 司徒正美
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Stack Overflow Blog
Stack Overflow Blog
The GitHub Blog
The GitHub Blog
Google DeepMind News
Google DeepMind News
Security Archives - TechRepublic
Security Archives - TechRepublic
宝玉的分享
宝玉的分享
N
News and Events Feed by Topic
The Hacker News
The Hacker News
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
F
Full Disclosure
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
H
Hacker News: Front Page
L
LangChain Blog
Microsoft Security Blog
Microsoft Security Blog
Y
Y Combinator Blog
B
Blog RSS Feed
H
Heimdal Security Blog
Google Online Security Blog
Google Online Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 三生石上(FineUI控件)
V2EX - 技术
V2EX - 技术
V
Vulnerabilities – Threatpost
Help Net Security
Help Net Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Tailwind CSS Blog
W
WeLiveSecurity
T
Tenable Blog
D
DataBreaches.Net
Martin Fowler
Martin Fowler
Cyberwarzone
Cyberwarzone
Cisco Talos Blog
Cisco Talos Blog
S
Secure Thoughts
O
OpenAI News
L
LINUX DO - 热门话题
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Jina AI
Jina AI
J
Java Code Geeks
Know Your Adversary
Know Your Adversary
IT之家
IT之家
Latest news
Latest news
Cloudbric
Cloudbric

Cyberia

《C(金钱掌控)》观后感 - Cyberia C#在Windows UI开发中的衰落 - Cyberia 存储退潮 字体padding计算器 开源已死 如何写出优雅完整的音频元数据 自动化EAC音频抓取 起夜级家庭网络正在变得越来越重要 优化Powershell的启动时间 无JavaScript其实没有很重要 你不需要一个电纸书阅读器 文件分享的难题 为什么我不敢看都市 某科学的 SSH 算号指南 时代的辉夜姬 为什么 Pixi 是神 关于神椿的过去,当下以及未来,我的一点看法 Tahoe 的图标令人难评 The EVE of PUSHING LIMITS 在 Word 中通过制表符实现居中对齐等操作 编码之殇:前 Unicode 时代的 WAV 处理 WR1800K AX NAND 路由器刷入 Immortalwrt 一个 PWM 的世界 沉痛悼念复选框同志 字体焕新 独立博客自省问卷15题 答一答 解决 Visual Studio Code 内终端启动报错问题 (1) 简评《鱿鱼游戏》 Sword Art Online,以及它带来了什么 解决 Visual Studio Code 内终端启动报错问题 2024 年度总结 轻小说重度依赖 铃音之后 解决部分应用在 Windows 平台下端口被随机占用的问题 计算机科学中最困难的事:居中 宿舍场景下多人共用路由器下流量分流实践 Extra Decal Pre Processor 自动化处理 Amazon Music 文件 MD5 不一致问题 解决 MultiMC 无 Forge 版本的 Optifine 安装问题
sHELL 即地狱
Nullpinter · 2025-07-14 · via Cyberia

请注意

所有 PowerShell 指的是 pwsh7。

Shell#

Shell 是个老东西了。我想,没有人会喜欢写 Bash 脚本的:

# 变量赋值:等号前后不能有空格,违反直觉

name="Harusaruhi"

# name = "Harusaruhi" # 这样写会报错!

# 方括号内必须有空格,否则报错

if [ $name = "Harusaruhi" ]; then

echo "匹配"

fi

# if [$name = "Harusaruhi"]; then # 错误:缺少空格

# if [ $name="Harusaruhi" ]; then # 错误:等号前后不能有空格

# 字符串比较和数值比较用不同符号

num1=10

num2=20

if [ $num1 -lt $num2 ]; then # 数值比较用 -lt

echo "数值比较"

fi

if [ "$num1" \< "$num2" ]; then # 字符串比较用 \< (需要转义)

echo "字符串比较"

fi

# 数学运算的多种诡异语法

result1=$((num1 + num2)) # 方式1:双括号

result2=`expr $num1 + $num2` # 方式2:expr命令

79 collapsed lines

result3=$(expr $num1 + $num2) # 方式3:$()包裹expr

echo "结果: $result1, $result2, $result3"

# 字符串处理的反人类语法

text="Hello World"

echo ${text#Hello } # 删除前缀

echo ${text%World} # 删除后缀

echo ${text/World/Universe} # 替换

# 数组的诡异语法

arr=("apple" "banana" "orange")

echo ${arr[@]} # 访问所有元素 - @符号的含义不明

echo ${#arr[@]} # 数组长度 - 完全无法直观理解

# 参数?

echo "脚本名: $0"

echo "参数个数: $#"

echo "所有参数: $@"

echo "上个命令退出状态: $?"

# 循环语法的不一致性

# for循环有多种奇怪的形式

for i in 1 2 3; do

echo "数字: $i"

done

# 另一种for循环语法

for ((i=1; i<=3; i++)); do

echo "计数: $i"

done

# while循环

counter=1

while [ $counter -le 3 ]; do

echo "While: $counter"

counter=$((counter + 1))

done

# 函数定义和调用

function_name() {

# 函数参数通过$1, $2...访问,没有参数名

echo "函数参数1: $1"

echo "函数参数2: $2"

# 返回值只能是数字,且通过$?获取

return 114514

}

# 调用函数

function_name "hello" "world"

echo "函数返回值: $?"

# 奇怪语法

if [ -f "file.txt" ]; then

echo "文件存在"

fi

if [ -d "directory" ]; then

echo "目录存在"

fi

# 引号

echo 'Single quotes: $name will not be expanded'

echo "Double quotes: $name will be expanded"

echo `Command substitution with backticks`

echo $(Command substitution with dollar parentheses)

# 逻辑运算符

if [ $num1 -gt 5 ] && [ $num2 -lt 30 ]; then

echo "逻辑与"

fi

if [ $num1 -gt 5 -a $num2 -lt 30 ]; then

echo "另一种逻辑与"

fi

echo "当前进程ID: $$"

echo "后台进程ID: $!"

echo "当前选项: $-"

echo "随机数: $RANDOM"

其抽象的操作符和滥用的特殊符号,可能在当时非常重要,因为存储空间和屏幕都非常有限,但是在现代只会导致代码可读性极差并且维护困难。函数设计也非常的 Shell,通过数字 index 来访问参数更是让整个逻辑的理解难上加难,没有一点信息量。

然后我们有了 PowerShellNushell

走进新时代:面向对象与数据驱动#

微软,Microsoft,我们罪该万死的 M$,终于没有遵循可笑的 Unix 哲学,终于引入了对象,淘汰了文本流。我们现在有了:

function Process-UserData {

param(

[string]$Username, # 类型!

[string]$Email, # 变量名!

[string]$OutputFile

)

# 面向对象的操作

$user = Get-User -Name $Username # 可读的 Builtin Command!

$user.Email = $Email

$user | Export-Csv -Path $OutputFile

}

但是,遗老们就开始发力了。我不明白为什么有些人会故意的把 *sh 写的晦涩难懂,故意使用缩写而不是完整的 argument 和 option:

find . -name "*.log" -mtime +7 -exec rm {} \;

grep -r "ERROR" /var/log|cut -d: -f1|sort|uniq -c|sort -nr

仅仅展开缩写就能够极大的提高可读性和可维护性:

find . --name "*.log" --mtime +7 --exec rm {} \;

grep --recursive "ERROR" /var/log | cut --delimiter=: --fields=1 | sort | uniq --count | sort --numeric-reverse

真正的 Unix 用户只需要 grep、sed、awk 就能解决99%的问题,为什么要用一个臃肿的对象系统?
Unix 的管道哲学是用简单工具组合复杂功能,PowerShell 却想用一个复杂工具解决所有问题
这种设计哲学与 Unix 的极简主义背道而驰

这些都是常见的言论,我只能说 COM 设计的比 Pipeline + FileSink 好不是没有原因的。

不止 PowerShell,Nushell 也添加了基本的类型系统(尽管并不丰富):Integers、Strings、Tables、Any……一个完备可用的类型系统能够极大的提高开发和除错的效率,使得整个流程更加可控、现代化。我们必须认识到,大部分的数据都不是文本和数据流,而是有结构的:使用awk处理一个有向无环图试试?

类型系统和对象的引入使得编写的脚本更加健壮,更加结构化;IDE 也能够通过类型信息自动生成文档和提供提示。

结构化数据处理#

简单工具组合复杂功能相比,现代 Shell 可太好用了:

curl -s "https://api.github.com/repos/octocat/Hello-World" | \

grep '"name"' | \

head -1

$repoInfo = Invoke-RestMethod "https://api.github.com/repos/octocat/Hello-World"

$repoInfo.name

# 其他

11 collapsed lines

# 直接处理CSV

$csvData = Import-Csv "employees.csv"

$csvData | Where-Object {$_.Department -eq "IT"} | Select-Object Name, Salary

# 直接处理XML

$xmlData = [xml](Get-Content "config.xml")

$xmlData.configuration.appSettings.add | Where-Object {$_.key -eq "DatabaseUrl"}

# 直接处理Excel文件

$excelData = Import-Excel "report.xlsx"

$excelData | Group-Object Department | Select-Object Name, Count

http get "https://api.github.com/repos/nushell/nushell" | get name

# 其他

11 collapsed lines

# 处理CSV数据

open employees.csv | where department == "IT" | select name salary

# 处理YAML配置

open config.yaml | get database.connections | where active == true

# 处理TOML文件

open Cargo.toml | get dependencies | columns

# 处理XML数据

open data.xml | get root.items.item | where status == "active"

臃肿的对象系统能够带来类型安全、性能优化以及更好的错误处理。直接加载结构化数据的能力代表了 Shell 脚本的一个重要演进方向。它不仅提高了开发效率,还大大降低了出错概率,使得复杂的数据处理任务变得简单直观。这是软件工程向更高层次抽象发展的必然结果。正如我们不再用汇编语言编写应用程序一样,在数据处理领域,我们也应该拥抱更高效、更安全的工具和方法。

Builtins:告别依赖地狱#

传统的 Unix Shell 信奉”小工具组合”,结果就是每个脚本都要依赖一堆外部工具。写个处理 JSON 的脚本?你需要 curljqheadtailgrepsedawk……天知道用户的系统上有没有装这些东西,更别说版本兼容性了。

curl -s "$API_URL" | jq '.items[]' | head -10

find /tmp -name "*.log" -exec gzip {} \;

date -d "2024-01-01" +%s # GNU date

date -j -f "%Y-%m-%d" "2024-01-01" +%s # BSD date,完全不同的语法!

这种设计带来的问题显而易见:环境依赖版本差异安全风险性能损失。每个外部命令都是一个新进程,每个新进程都是潜在的攻击面。

现代 Shell 的内置命令解决了这些问题:

$data = Invoke-RestMethod $API_URL

$data.items | Select-Object -First 10

Get-ChildItem /tmp -Filter "*.log" | Compress-Archive

Get-Date "2024-01-01" | Get-Date -UFormat %s # 跨平台一致

http get $API_URL | get items | first 10

ls /tmp | where name =~ "\.log$" | each { gzip $in.name }

"2024-01-01" | into datetime | format date "%s"

  • 安全性提升:内置命令无法被外部劫持,which curl 可能返回恶意替代品,但 Get-Command Invoke-RestMethod 总是返回可信的内置命令。
  • 性能优化:传统方式处理1000个文件需要创建3000个进程,现代内置命令在同一进程内完成所有操作。
  • 一致性保证:不再需要处理 GNU coreutils 和 BSD 工具之间的差异,内置命令在所有平台上行为一致。

现代 Shell 的内置命令不是臃肿,而是必要的现代化。它们提供了类型安全、错误处理、性能优化和一致性保证,这些都是传统外部工具无法提供的。正如我们不再用汇编语言写应用程序一样,在 Shell 脚本领域,我们也应该拥抱更高效、更安全的内置工具集。

Deno Shell#

deno_task_shell 是一个跨平台的 Shell 解析器,最初为 deno task 设计,但现在被许多应用嵌入使用。dax 库基于 deno_task_shell 的解析器,提供了在 Linux、Mac 和 Windows 上一致的 Shell 语法支持。

这种嵌入式 Shell 的设计理念确实很符合现代应用的需求:不是要做一个完整的 Shell,而是提供一个可预测、跨平台的 Shell 子集,让应用开发者不用再为不同操作系统的 Shell 差异而烦恼。

可移植性和可预测性比完整功能更重要。我们不需要一个功能完备的 Shell,我们需要的是一个在任何地方都能正常工作的 Shell。

新的问题#

说了这么多好处,难道就没有缺点吗?有的,主要是大。Nushell的典型 musl 分发大小在 60 MiB 左右,这使得其不能够在路由器、嵌入式上正常使用。PowerShell 更是直接分发了 .NET RT,以提供完整的CLR支持(是的你可以在 pwsh 中调用 C# 和玩反射)。相比之下,传统的 /bin/sh 可能只有几百 KB,bash 也不过几 MB。

这在软件设计中是一个永恒的矛盾。现代 Shell 为了提供类型安全、丰富的内置命令和跨平台支持,基本上不可能拥有小的体积,但是这些嵌入式应用场景,反而在一定程度上正是 Shell 的用武之地。

依旧是地狱#

说了这么多,现代的 sHELL 在哪里呢?在源里。没有一个是某个操作系统的默认 shell,这使得其脚本分发变得非常困难。现代的 Shell 很难到最需要它们的地方去,我们依然在处理可能在千禧年就处理过的问题。

相关资源: