美文网首页
【译】Shell 入门

【译】Shell 入门

作者: zhangPeng丶 | 来源:发表于2019-02-17 02:43 被阅读9次

    本文为译文,原文链接shell

    shell是一个高效的、文本化的计算机接口。

    shell提示符:当你打开终端时看到的一切。可以让用户执行的程序和命令,常见的有:

    • cd改变目录
    • ls列出文件和目录
    • mvcp移动和复制文件

    但是shell允许您做更多的事情;您可以调用计算机上的任何程序,并且命令行工具的存在就是为了完成您可能想做的任何事情。他们往往比他们的图形对手更有效率。我们这门课会讲到很多。

    shell提供交互式编程语言 ("脚本")。有很多种shell

    • 您可能用过sh或者bash
    • 和语言相关的shellcsh
    • 或者更好用的shell:fishzshksh

    在这个课堂上,我们将关注无处不在的shbash,但是使用其他的shell感觉更好。我喜欢fish

    在您的工具箱中,shell程序是一个非常有用的工具。可以直接在提示符下编写程序,也可以将程序写入文件。

    通过#!/bin/sh+chmod +x将shell程序变成可以执行的

    使用shell工作

    将一个命令运行多次:

    for i in $(seq 1 5); do echo hello; done
    

    有很多东西可以展开来讲:

    • for x in list; do BODY; done

      • ;终止一个命令 -- 相当于换行
      • 遍历list,将每个值赋值给x,然后运行
      • 分割标志符是“空格”,我们稍后会讲到
      • shell中没有花括号,所以使用do+done
    • $(seq 1 5)

      • 运行seq命令,参数分别为1和5
      • 使用括号内命令的输出替换 $()
      • 相当于
       for i in 1 2 3 4 5
      
    • echo hello

      • shell脚本中的所有内容都是命令
      • 在本例中,运行echo命令,将打印该命令的参数hello
      • 所有命令都可以在$PATH搜索到

    我们可以举个例子:

    for f in $(ls); do echo $f; done
    

    将打印当前目录中的每个文件名。可以使用=设置变量的值(=两边不需要空格)

    foo=bar
    echo $foo
    

    这里也有一些特殊的变量:

    • $1-$9:脚本的参数
    • $0:脚本的名称
    • $#:脚本的参数个数
    • $$:当前脚本的进程ID

    只打印目录

    for f in $(ls); do if test -d $f; then echo dir $f; fi; done
    

    这里展开来讲:

    • if CONDITION; then BODY; fi
      • CONDITION是一个命令,如果返回时为0(success),就会执行BODY
      • 也可以继续执行else或者elif
      • 同样,没有花括号,所以使用thenfi
    • test是另外一个命令,提供各式各样的检查与对比功能,退出时会返回对比结果,如果为真,则返回0($?)
      • man COMMAND会对您有很大的帮助,比如:man test
      • 也可以使用[+]执行,比如:[ -d $f ]
        • 查看一下man testwhich [的执行结果

    可是等等!结果是错误的!如果有个文件叫做“我的文档怎么办”?

    • for f in $(ls)展开为for f in My Documents
    • 先以Mytest的执行参数,然后以Documents作为参数
    • 这不是我们想要的!
    • shell脚本中导致出现问题最多的原因

    参数分割

    Bash是通过空格分割参数;但这并不总是您想要的!

    • 需要使用引号处理for f in "My Documents"f的空格,才能正确的执行
    • 其他地方也有同样的问题,您看到过吗?比如test -d $f:如果$f中包含空格,test将会发生错误!
    • echo碰巧没有问题,因为按空格分隔联接,但是如果文件名包含换行符,怎么办?!变成空格!
    • 引号用于所有不希望被拆分的参数
    • 我们该如何修复上面的脚本呢?您认为for f in "$(ls)"怎么样?

    答案是通配符!

    • Bash知道如何使用模板查找文件:
      • *任意字符串
      • ?任意字符
      • {a,b,c}这些字符中的任意一个
    • for f in *:这个文件夹下所有的文件
    • 在使用通配符时,每个匹配的文件都将变成自己的参数
      • 在使用时,仍需要确保引号的正确使用:test -d "$f"
    • 可以使用这些提高通配符效率:
      • for f in a*: 当前文件夹下,所有以a开头的文件
      • for f in foo/*.txt:foo文件夹下,所有以.txt结尾的文件
      • for f in foo/*/p??.txt: 在foo的子文件夹下,以p开头的三个字母的文件

    空格的问题不止于此:

    • if [ $foo = "bar" ]; then-- 看看这个问题?
    • 如果$foo是空的呢?[的参数是=bar...
    • 可以用[ x$foo = "xbar" ]来解决这个问题,但是效率低
    • 相反,使用[[:一个bash内置的具有特殊解析的比较器
      • 也可以使用&&代替-a-o连接||等等

    可组合性

    Shell之所以强大,部分原因在于它的可组合性。可以将多个程序链接在一起,而不是让一个程序做每一件事情。

    关键字是|

    a|b表示同时运行ab,将a的所有输出,当作b的输入,打印b的输出。

    您启动的所有程序(“进程”)都有三个“流”:

    • STDIN:当程序读取输入时,它从这里开始
    • STDOUT:当程序打印东西时,它就在这里
    • STDERR:程序可以选择使用的第二个输出
    • 默认的,STDIN是您的键盘输入,STDOUTSTDERR都是您的终端。但是您可以改变他们!
      • a | ba的输出当作b的输入
      • 同样还有:
        • a > foo(将a的标准输出写入foo文件)
        • a 2> foo(将a的标准错误输出写入foo文件)
        • a < fooa的标准输入是从foo文件读取的)
        • 提示:tail -f将打印文件内容,即使他正在被写入

    为什么这个这么有用?您亲自试试下面程序的输出!

    • ls | grep foo:包含单词foo的所有文件
    • ps | grep foo:包含单词foo的所有进程
    • journalctl | grep -i intel | tail -n5:最后5条带有intel(不区分大小写)的系统日志消息
    • who | sendmail -t me@example.com:将登录用户列表发送到me@example.com
    • 形成了许多数据处理的基础,稍后我们将讨论它

    Bash还提供了许多其他编写程序的方法。

    您可以组合形成一个命令(a; b) | tac:先运行a,然后运行b,然后把他们的所有输出当作tac命令的输入,tac是一个将输入反序的命令。

    一个不太为人所知但超级有用的方法是过程替换。b <(a)将运行a,为输出流生成一个临时文件名,并将该文件名传递给b。举个例子:

    diff <(journalctl -b -1 | head -n20) <(journalctl -b -2 | head -n20)
    

    将向您展示前一个引导日志的前20行与更前一个引导日志的前20行之间的区别。

    任务和进程控制

    如果您在后台执行周期更长的任务呢?

    • 在后台运行的程序是以&结尾
      • 它会立即给你提示
      • 如果你想同时运行两个程序,比如服务器和客户端,这很好解决:server & client
      • 注意:正在运行的程序仍将终端设置为标准输出,试一试:server > server.log & client
    • 通过jobs查看所有的进程
      • 注意现实Running
    • 使用fg %JOB将其放到前台(没有参数是最新的)
    • 如果您想将当前的程序放入后台:^Z+bg(这里的^Z代表按Ctrl+Z)
      • ^Z将当前的进程停止,并将它变成一个job
      • bg将最新的job在后台运行(就像使用了&
    • 后台jobs仍然绑定到当前会话,如果注销,则退出。您可以使用disown或者nohup切断这种绑定关系。
    • $!是最后一个后台进程的pid

    在你的电脑上运行的其他东西呢?

    • ps很好用:列出正在运行的进程
      • ps -A:打印所有用户的进程(也包括ps ax)
      • ps有很多参数:可以通过man ps查看
    • pgrep:搜索进程(和ps -A | grep类似)
      • pgrep -af:通过参数搜索和显示
    • kill:通过ID向进程发送信号(pkill by search + -f)
      • 信号告诉进程“做什么事”
      • 最常见:SIGKILL(-9-KILL):立刻退出,相当于^\
      • 还有:SIGTERM(-15or-TERM):立刻优雅的退出,相当于^C

    标志符

    大多数命令行程序都使用标志符接受参数。标志符通常有短形式(-h)和长形式(--help)。通常运行CMD -hman CMD会给你展示该CMD可用的标识符的列表。短标志通常可以组合使用,运行rm -r -f相当于运行rm -rf或者rm -fr。一些常见的标识符是有约定俗成的标准的,您会发现它们在很多命令中:

    • -a一般指所有文件(也包括那些以点开头的)
    • -f通常指强制做什么事情,比如说rm -f
    • -h大多数命令都是显示帮助
    • -v通常启用详细输出
    • -V通常打印命令的版本

    此外,双破折号--用于内置命令和许多其他命令中,表示命令选项的结束,之后只接受位置参数。因此,如果您有一个可以使用-v参数的文件(文件类型支持使用),并且想要grep它,grep pattern -- -v可以,但是grep pattern -v不行。事实上,创建这种文件的方法是touch -- -v

    附录

    1. shell
    2. Shell笔记
    3. shell 中各种括号的作用
    4. Shell test 命令

    title: 【译】Shell 入门教程

    author: zhangpeng

    date: 2019.02.17

    GitHub: https://github.com/fullstack-zhangpeng

    相关文章

      网友评论

          本文标题:【译】Shell 入门

          本文链接:https://www.haomeiwen.com/subject/zseqeqtx.html