Unix 哲学强调构建简单且可扩展的软件。每个软件都必须做一件事,并且把它做好。该软件应该能够通过一个通用接口——文本流——与其他程序协同工作。这是 Unix 的核心哲学之一,它使它变得如此强大和直观易用。
这是摘自The Unix Programming Environment
尽管 UNIX 系统引入了许多创新的程序和技术,但没有任何一个程序或想法可以使它运行良好。相反,使它有效的是编程方法,一种使用计算机的哲学。虽然这种哲学不能用一句话写下来,但其核心思想是系统的力量更多地来自于程序之间的关系,而不是程序本身。许多 UNIX 程序孤立地做一些非常琐碎的事情,但是,与其他程序结合起来,就变成了通用和有用的工具。
我认为这很好地解释了这一点。另外,请观看 Brian Kernighan的完整演讲,他解释了 UNIX 操作系统的基础知识,他还通过一个使用管道的示例。
不过,在这篇文章中,我想展示一些实践这一哲学的例子——一个人如何结合使用不同的 unix 工具来完成一些强大的事情。
例子:
根据对 git 仓库的提交数量打印作者排行榜
从 IMDb 列表中随机获取电影
让我们从一个简单的开始——显示一个基于提交数量排序的 git 仓库的作者/贡献者列表,并按降序对列表进行排序(大多数提交贡献在顶部)。当您从管道的角度考虑时,这是一项简单的任务。git log
用于显示提交日志。我们可以将--format=
选项传递给它并提及我们希望以何种格式显示提交。--format='%an'
只需为每个提交打印作者姓名。
$ git log --format='%an'AliceBobDeniseDeniseCandiceDeniseAliceAliceAlice
现在我们可以使用该sort
实用程序按字母顺序对它们进行排序。
$ git log --format='%an' | sortAliceAliceAliceAliceBobCandiceDeniseDeniseDenise
接下来我们使用uniq
$ git log --format='%an' | sort | uniq -c 4 Alice 1 Bob 1 Candice 3 Denise
根据 的uniq
手册页:
uniq - 报告或省略重复行
从 INPUT(或标准输入)中过滤相邻的匹配行,写入 OUTPUT(或标准输出)。
所以uniq
打印出重复的行,但只打印出相邻的行。这就是为什么我们必须先将输出传递给sort
. 该-c
标志在每行前面加上出现次数。
您可以看到输出仍然按字母顺序排序。所以现在剩下的就是按数字排序。sort
在flag 中有一个标志-n
。它根据数值来考虑数字。
$ git log --format='%an' | sort | uniq -c | sort -nr 4 Alice 3 Denise 1 Candice 1 Bob
该-r
标志还包括在内以相反的顺序打印列表。默认情况下,它按升序排序。你有他们的——根据提交次数排序的作者列表。
您知道吗,只需将“ .json
”附加到 reddit url 即可获得 json 响应,而不是通常的 html?这允许一个充满可能性的世界!其中之一是直接从命令行浏览模因(当然不完全是,因为实际图像将显示在 GUI 程序上)。我们可以简单地 curl 或 wget url – https://reddit.com/r/memes.json
$ wget -O - -q 'https://reddit.com/r/memes.json''{"kind": "Listing", "data": {"modhash": "xyloiccqgm649f320569f4efb427cdcbd89e68aeceeda8fe1a", "dist": 27, "children":[{"kind": "t3", "data": {"approved_at_utc": null, "subreddit": "memes","selftext": "More info available at....'......More lines......
我在这里使用 wget 是因为 Curl User-Agent 似乎得到了不同的对待。显然,您可以通过简单地更改“User-Agent”标头来解决这个问题,但我只是选择了wget
. Wget 有一个-O
提供输出文件名。大多数采用此类选项的程序还允许其值-
表示标准输出或输入,具体取决于上下文。该-q
选项只是告诉 wget 安静并且不打印进度状态之类的东西。现在我们得到了一个大的 JSON 结构来使用。现在,要在命令行上有意义地解析和使用此 JSON 数据,我们可以使用 jq
. jq
可以被认为是JSON 的sed
/ awk
。它有自己的简单直观语言,您可以从它的手册页中参考。
如果您查看响应 JSON,它看起来像这样:
{ "kind": "Listing", "data": { "modhash": "awe40m26lde06517c260e2071117e208f8c9b5b29e1da12bf7", "dist": 27, "children": [], "after": "t3_gi892x", "before": null }}
所以这里我们有一些“Listing”类型的响应,我们可以看到我们有一个“children”数组。该数组的每个元素都是一篇文章。
这是“children”数组的元素之一:
{ "kind": "t3", "data": { "subreddit": "memes", "selftext": "", "created": 1589309289, "author_fullname": "t2_4amm4a5w", "gilded": 0, "title": "Its hard to argue with his assessment", "subreddit_name_prefixed": "r/memes", "downs": 0, "hide_score": false, "name": "t3_gi8wkj", "quarantine": false, "permalink": "/r/memes/comments/gi8wkj/its_hard_to_argue_with_his_assessment/", "url": "https://i.redd.it/6vi05eobdby41.jpg", "upvote_ratio": 0.93, "subreddit_type": "public", "ups": 11367, "total_awards_received": 0, "score": 11367, "author_premium": false, "thumbnail": "https://b.thumbs.redditmedia.com/QZt8_SBJDdKLVnXK8P4Wr_02ALEhGoGFEeNhpsyIfvw.jpg", "gildings": {}, "post_hint": "image", ".................." "more lines skipped" ".................." }}
我减少了data
. 总共有 105 个项目。如您所见,您可以获取有关帖子的许多有趣的数据属性。我们感兴趣的是url
帖子。这不是实际的 reddit 帖子的 url,而是帖子内容的 url。如果帖子 url 是你想要的,那就是permalink
. 所以在这种情况下,该 url
字段是 meme 图像的 url。
我们可以使用以下方法简单地获取每个帖子的所有 url 的列表:
$ wget -O - -q reddit.com/r/memes.json | jq '.data.children[] |.data.url'"https://www.reddit.com/r/memes/comments/g9w9bv/join_the_unofficial_redditmc_minecraft_server_at/""https://www.reddit.com/r/memes/comments/ggsomm/10_million_subscriber_event/""https://i.imgur.com/KpwIuSO.png""https://i.redd.it/ey1f7ksrtay41.jpg""https://i.redd.it/is3cckgbeby41.png""https://i.redd.it/4pfwbtqsaby41.jpg"......
忽略前两个链接,它们基本上是模组放置的粘性帖子,其“url”与“永久链接”相同。
jq
从标准输入中读取并提供我们之前看到的 JSON。 .data.children
指的是我之前提到的一系列帖子。And –.data.children[] | .data.url
表示“遍历数组中的每个元素并打印每个元素的‘data’字段中的‘url’字段”。
所以我们得到了/r/memes的“热门”帖子的所有 url 列表 。如果您想获得本周的“热门”帖子,那么您可以点击https://reddit.com/r/memes/top.json?t=week。对于所有时间的顶级职位?t=all
, 年?t=year
等等。
一旦我们有了所有 URL 的列表,我们现在就可以将它通过管道传输到xargs
. Xargs 是一个非常有用的实用程序,可以从标准输入构建命令行。这是 xarg 的手册页所说的:
xargs 从标准输入中读取项目,由空格(可以用双引号或单引号或反斜杠保护)或换行符分隔,并执行命令(默认为 /bin/echo)一次或多次,后跟任何初始参数通过从标准输入读取的项目。标准输入上的空行被忽略
所以运行类似:
$ echo "https://i.redd.it/4pfwbtqsaby41.jpg" | xargs wget -O meme.jpg -q
相当于运行:
$ wget -O meme.jpg -q "https://i.redd.it/4pfwbtqsaby41.jpg"
现在,我们可以将 URL 列表传递给图像查看器,例如 feh
接受 eog
URL 作为有效参数的图像查看器。
$ wget -O - -q reddit.com/r/memes.json | jq '.data.children[] |.data.url' | xargs feh
现在,feh 弹出模因,我可以使用箭头键浏览它们,就像它们在我的本地磁盘上一样。
铁丝网
或者我可以简单地使用 wget 下载所有图像,替换 feh
为wget
上面的内容。
可能性是无穷无尽的。这个 reddit JSON 数据的另一个很好的用途是 将你的桌面壁纸设置为来自“热门”部分的/r/earthporn的最高投票图像 。
$ wget -O - -q reddit.com/r/earthporn.json | jq '.data.children[] |.data.url' | head -1 | xargs feh --bg-fill
然后,如果需要,您可以将其设置为大约每小时运行一次的 cron-job。我在这里使用head
命令只打印第一行,这将是最受好评的帖子。就其本身而言,head
似乎做了一些非常琐碎和无用的事情,但在这种情况下,与其他程序一起工作,它就成为一个重要的部分。
你看到 Unix 流水线的威力了吗?一行代码完成了所有工作,从获取 JSON 数据、解析并从中获取相关数据,然后再次从 URL 获取图像,最后将其设置为墙纸。
我用它做的另一件愚蠢的事情是每两个小时从 /r/memes 下载模因。这是在我的机器上设置为 cron 作业。现在我有大约 19566 个模因占用了我磁盘上的 4.5G。我为什么要那样做?不要问我…
示例 3 - 从 IMDb 列表中获取随机电影让我们以一个简单的结束它。IMDb 具有允许您制作列表的功能。您还可以找到其他用户制作的列表。例如 -让您大开眼界的电影。如果您附加/export
到 url,您将获得某种格式的列表.csv
。
$ curl https://www.imdb.com/list/ls020046354/exportPosition,Const,Created,Modified,Description,Title,URL,Title Type,IMDb Rating,Runtime (mins),Year,Genres,Num Votes,Release Date,Directors1,tt0137523,2017-07-30,2017-07-30,,Fight Club,https://www.imdb.com/title/tt0137523/,movie,8.8,139,1999,Drama,1780706,1999-09-10,David Fincher2,tt0945513,2017-07-30,2017-07-30,,Source Code,https://www.imdb.com/title/tt0945513/,movie,7.5,93,2011,"Action, Drama, Mystery, Sci-Fi, Thriller",471234,2011-03-11,Duncan Jones3,tt0482571,2017-07-30,2017-07-30,,The Prestige,https://www.imdb.com/title/tt0482571/,movie,8.5,130,2006,"Drama, Mystery, Sci-Fi, Thriller",1133548,2006-10-17,Christopher Nolan4,tt0209144,2018-01-16,2018-01-16,,Memento,https://www.imdb.com/title/tt0209144/,movie,8.4,113,2000,"Mystery, Thriller",1081848,2000-09-05,Christopher Nolan5,tt0144084,2018-01-16,2018-01-16,,American Psycho,https://www.imdb.com/title/tt0144084/,movie,7.6,101,2000,"Comedy, Crime, Drama",462984,2000-01-21,Mary Harron6,tt0364569,2018-01-16,2018-01-16,,Oldeuboi,https://www.imdb.com/title/tt0364569/,movie,8.4,120,2003,"Action, Drama, Mystery, Thriller",491476,2003-11-21,Chan-wook Park7,tt1130884,2018-10-08,2018-10-08,,Shutter Island,https://www.imdb.com/title/tt1130884/,movie,8.1,138,2010,"Mystery, Thriller",1075524,2010-02-13,Martin Scorsese8,tt8772262,2019-12-27,2019-12-27,,Midsommar,https://www.imdb.com/title/tt8772262/,movie,7.1,148,2019,"Drama, Horror, Mystery, Thriller",150798,2019-06-24,Ari Aster
我们可以使用cut
来决定我们需要打印哪些字段:
$ curl https://www.imdb.com/list/ls020046354/export | cut -d ',' -f 6TitleFight ClubSource CodeThe PrestigeMementoAmerican PsychoOldeuboiShutter IslandMidsommar
该-d
选项是为每个字段指定分隔符。字段用什么分隔?在这种情况下,它是一个逗号 ( ,
)。该-f
选项是您要打印的字段编号。在本例中,第六个字段是电影的标题。这也会打印 csv 标题“Title”,因此要删除它,我们可以使用 ,sed '1 d'
这意味着从输入流中删除1行。
然后我们可以将电影列表通过管道传输到shuf
. Shuf 只是随机打乱它的输入行并将其吐出。
$ curl https://www.imdb.com/list/ls020046354/export | cut -d ',' -f 6 | sed '1 d' | shufAmerican PsychoMidsommarSource CodeOldeuboiFight ClubMementoShutter IslandThe Prestige
现在只需将其通过管道传输到head -1
or中sed '1 q'
,它只会打印第一行。每次你运行这个,你应该得到一个随机选择。
$ curl https://www.imdb.com/list/ls020046354/export | cut -d ',' -f 6 | sed '1 d' | shuf | head -1Source Code
现在假设您还希望 URL 与标题一起打印,没问题,cut
允许您指定要打印的多个字段--field=LIST
$ curl https://www.imdb.com/list/ls020046354/export | cut -d ',' --field=6,7 | sed '1 d' | shuf | head -1Shutter Island,https://www.imdb.com/title/tt1130884/
但是这有一个问题,如果电影标题中有一个逗号,那么您将得到一个完全不同的字段值。克服这个问题的一种方法是使用像这样的 python 单行代码:
python -c 'import csv,sys;[print (a["Title"]) for a in csv.DictReader(sys.stdin)]'
$ curl -s https://www.imdb.com/list/ls020046354/export |\ python -c 'import csv,sys;[print (a["Title"],a["URL"]) for a in csv.DictReader(sys.stdin)]'|\ shuf | head -1Oldeuboi https://www.imdb.com/title/tt0364569/
这些只是几个示例,您可以使用管道在一行 shell 中完成很多事情。
关注微信公众号获取更多VSCode编程信息,定时发布干货文章
全部评论