简单 Shell编程学习

世界上对 shell 脚本最好的概念性介绍来自一个老的 AT&T 培训视频 。在视频中,Brian W. Kernighan(awk 中的“k”),Lorinda L. Cherry(bc 做者之一)论证了 UNIX 的基础原则之一是让用户利用现有的实用程序来定制和建立复杂的工具。html

Kernighan 的话来讲:“UNIX 系统程序基本上是 …… 你能够用来创造东西的构件。…… 管道的概念是 [UNIX] 系统的基础;你能够拿一堆程序 …… 并将它们端到端链接到一块儿,使数据从左边的一个流到右边的一个,由系统自己管着全部的链接。程序自己不知道任何关于链接的事情;对它们而言,它们只是在与终端对话。”

他说的是给普通用户以编程的能力。linux

POSIX 操做系统自己就像是一个 API。若是你能弄清楚如何在 POSIX 的 shell 中完成一个任务,那么你能够自动化这个任务。这就是编程,这种平常 POSIX 编程方法的主要方式就是 shell 脚本。程序员

像它的名字那样,shell 脚本就是一行一行你想让你的计算机执行的语句,就像你手动的同样。shell

由于 shell 脚本包含常见的平常命令,因此熟悉 UNIX 或 Linux(一般称为 POSIX 系统)对 shell 是有帮助的。你使用 shell 的经验越多,就越容易编写新的脚本。这就像学习外语:你内心的词汇越多,组织复杂的句子就越容易。编程

当您打开终端窗口时,就是打开了 shell 。shell 有好几种,本教程适用于 bashtcshkshzsh 和其它几个。在下面几个部分,我提供一些 bash 特定的例子,但最终的脚本不会用那些,因此你能够切换到 bash 中学习设置变量的课程,或作一些简单的语法调整bash

若是你是新手,只需使用 bash 。它是一个很好的 shell,有许多友好的功能,它是 Linux、Cygwin、WSL、Mac 默认的 shell,而且在 BSD 上也支持。app

Hello world

您能够从终端窗口生成您本身的 hello world 脚本 。注意你的引号;单和双都会有不一样的效果(LCTT 译注:想必你不会在这里使用中文引号吧)。ide

  1. $ echo "#\!/bin/sh" > hello.sh
  2. $ echo "echo 'hello world' " >> hello.sh

正如你所看到的,编写 shell 脚本就是这样,除了第一行以外,就是把命令“回显”或粘贴到文本文件中而已。工具

像应用程序同样运行脚本:学习

  1. $ chmod +x hello.sh
  2. $ ./hello.sh
  3. hello world

无论多少,这就是一个 shell 脚本了。

如今让咱们处理一些有用的东西。

去除空格

若是有一件事情会干扰计算机和人类的交互,那就是文件名中的空格。您在互联网上看到过:http://example.com/omg%2ccutest%20cat%20photophoto%21%211.jpg 等网址。或者,当你无论不顾地运行一个简单的命令时,文件名中的空格会让你掉到坑里:

  1. $ cp llama pic.jpg ~/photos
  2. cp: cannot stat 'llama': No such file or directory
  3. cp: cannot stat 'pic.jpg': No such file or directory

解决方案是用反斜杠来“转义”空格,或使用引号:

  1. $ touch foo\ bar.txt
  2. $ ls "foo bar.txt"
  3. foo bar.txt

这些都是要知道的重要的技巧,可是它并不方便,为何不写一个脚本从文件名中删除这些烦人的空格?

建立一个文件来保存脚本,以释伴shebang)(#!) 开头,让系统知道文件应该在 shell 中运行:

  1. $ echo '#!/bin/sh' > despace

好的代码要从文档开始。定义好目的让咱们知道要作什么。这里有一个很好的 README:

  1. despace is a shell script for removing spaces from file names.
  2. Usage:
  3. $ despace "foo bar.txt"

如今让咱们弄明白如何手动作,而且如何去构建脚本。

假设你有个只有一个 foo bar.txt 文件的目录,好比:

  1. $ ls
  2. hello.sh
  3. foo bar.txt

计算机无非就是输入和输出而已。在这种状况下,输入是 ls 特定目录的请求。输出是您所指望的结果:该目录文件的名称。

在 UNIX 中,能够经过“管道”将输出做为另外一个命令的输入,不管在管道的另外一侧是什么过滤器。 tr 程序刚好设计为专门修改传输给它的字符串;对于这个例子,可使用 --delete 选项删除引号中定义的字符。

  1. $ ls "foo bar.txt" | tr --delete ' '
  2. foobar.txt

如今你获得了所需的输出了。

在 Bash shell 中,您能够将输出存储为变量 。变量能够视为将信息存储到其中的空位:

  1. $ NAME=foo

当您须要返回信息时,能够经过在变量名称前面缀上美圆符号($ )来引用该位置。

  1. $ echo $NAME
  2. foo

要得到您的这个去除空格后的输出并将其放在一边供之后使用,请使用一个变量。将命令的结果放入变量,使用反引号(`)来完成:

  1. $ NAME=`ls "foo bar.txt" | tr -d ' '`
  2. $ echo $NAME
  3. foobar.txt

咱们完成了一半的目标,如今能够从源文件名肯定目标文件名了。

到目前为止,脚本看起来像这样:

  1. #!/bin/sh
  2. NAME=`ls "foo bar.txt" | tr -d ' '`
  3. echo $NAME

第二部分必须执行重命名操做。如今你可能已经知道这个命令:

  1. $ mv "foo bar.txt" foobar.txt

可是,请记住在脚本中,您正在使用一个变量来保存目标名称。你已经知道如何引用变量:

  1. #!/bin/sh
  2. NAME=`ls "foo bar.txt" | tr -d ' '`
  3. echo $NAME
  4. mv "foo bar.txt" $NAME

您能够将其标记为可执行文件并在测试目录中运行它。确保您有一个名为 foo bar.txt(或您在脚本中使用的其它名字)的测试文件。

  1. $ touch "foo bar.txt"
  2. $ chmod +x despace
  3. $ ./despace
  4. foobar.txt
  5. $ ls
  6. foobar.txt

去除空格 v2.0

脚本能够正常工做,但不彻底如您的文档所述。它目前很是具体,只适用于一个名为 foo\ bar.txt 的文件,其它都不适用。

POSIX 命令会将其命令自身称为 $0,并将其后键入的任何内容依次命名为 $1$2$3 等。您的 shell 脚本做为 POSIX 命令也能够这样计数,所以请尝试用 $1 来替换 foo\ bar.txt

  1. #!/bin/sh
  2. NAME=`ls $1 | tr -d ' '`
  3. echo $NAME
  4. mv $1 $NAME

建立几个新的测试文件,在名称中包含空格:

  1. $ touch "one two.txt"
  2. $ touch "cat dog.txt"

而后测试你的新脚本:

  1. $ ./despace "one two.txt"
  2. ls: cannot access 'one': No such file or directory
  3. ls: cannot access 'two.txt': No such file or directory

看起来您发现了一个 bug!

这实际上不是一个 bug,一切都按设计工做,但不是你想要的。你的脚本将 $1 变量真真切切地 “扩展” 成了:“one two.txt”,捣乱的就是你试图消除的那个麻烦的空格。

解决办法是将变量用以引号封装文件名的方式封装变量:

  1. #!/bin/sh
  2. NAME=`ls "$1" | tr -d ' '`
  3. echo $NAME
  4. mv "$1" $NAME

再作个测试:

  1. $ ./despace "one two.txt"
  2. onetwo.txt
  3. $ ./despace c*g.txt
  4. catdog.txt

此脚本的行为与任何其它 POSIX 命令相同。您能够将其与其余命令结合使用,就像您但愿的使用的任何 POSIX 程序同样。您能够将其与命令结合使用:

  1. $ find ~/test0 -type f -exec /path/to/despace {} \;

或者你可使用它做为循环的一部分:

  1. $ for FILE in ~/test1/* ; do /path/to/despace $FILE ; done

等等。

去除空格 v2.5

这个去除脚本已经能够发挥功用了,但在技术上它能够优化,它能够作一些可用性改进。

首先,变量实际上并不须要。 shell 能够一次计算所需的信息。

POSIX shell 有一个操做顺序。在数学中使用一样的方式来首先处理括号中的语句,shell 在执行命令以前会先解析反引号 ` 或 Bash 中的 $() 。所以,下列语句:

  1. $ mv foo\ bar.txt `ls foo\ bar.txt | tr -d ' '`

会变换成:

  1. $ mv foo\ bar.txt foobar.txt

而后实际的 mv 命令执行,就获得了 foobar.txt 文件。

知道这一点,你能够将该 shell 脚本压缩成:

  1. #!/bin/sh
  2. mv "$1" `ls "$1" | tr -d ' '`

这看起来简单的使人失望。你可能认为它使脚本减小为一个单行并无必要,但没有几行的 shell 脚本是有意义的。即便一个用简单的命令写的紧缩的脚本仍然能够防止你发生致命的打字错误,这在涉及移动文件时尤为重要。

此外,你的脚本仍然能够改进。更多的测试发现了一些弱点。例如,运行没有参数的 despace 会产生一个没有意义的错误:

  1. $ ./despace
  2. ls: cannot access '': No such file or directory
  3. mv: missing destination file operand after ''
  4. Try 'mv --help' for more information.

这些错误是让人迷惑的,由于它们是针对 lsmv 发出的,但就用户所知,它运行的不是 lsmv,而是 despace

若是你想想,若是它没有获得一个文件做为命令的一部分,这个小脚本甚至不该该尝试去重命名文件,请尝试使用你知道的变量以及 test 功能来解决。

if 和 test

if 语句将把你的小 despace 实用程序从脚本蜕变成程序。这里面涉及到代码领域,但不要担忧,它也很容易理解和使用。

if 语句是一种开关;若是某件事情是真的,那么你会作一件事,若是它是假的,你会作不一样的事情。这个 if-then 指令的二分决策正好是计算机是擅长的;你须要作的就是为计算机定义什么是真或假以及并最终执行什么。

测试真或假的最简单的方法是 test 实用程序。你不用直接调用它,使用它的语法便可。在终端试试:

  1. $ if [ 1 == 1 ]; then echo "yes, true, affirmative"; fi
  2. yes, true, affirmative
  3. $ if [ 1 == 123 ]; then echo "yes, true, affirmative"; fi
  4. $

这就是 test 的工做方式。你有各类方式的简写可供选择,这里使用的是 -z 选项,它检测字符串的长度是否为零(0)。将这个想法翻译到你的 despace 脚本中就是:

  1. #!/bin/sh
  2. if [ -z "$1" ]; then
  3. echo "Provide a \"file name\", using quotes to nullify the space."
  4. exit 1
  5. fi
  6. mv "$1" `ls "$1" | tr -d ' '`

为了提升可读性,if 语句被放到单独的行,可是其概念仍然是:若是 $1 变量中的数据为空(零个字符存在),则打印一个错误语句。

尝试一下:

  1. $ ./despace
  2. Provide a "file name", using quotes to nullify the space.
  3. $

成功!

好吧,其实这是一个失败,但它是一个漂亮的失败,更重要的是,一个有意义的失败。

注意语句 exit 1 。这是 POSIX 应用程序遇到错误时向系统发送警报的一种方法。这个功能对于须要在脚本中使用 despace ,并依赖于它成功执行才能顺利运行的你或其它人来讲很重要。

最后的改进是添加一些东西,以保护用户不会意外覆盖文件。理想状况下,您能够将此选项传递给脚本,因此它是可选的;但为了简单起见,这里对其进行了硬编码。 -i 选项告诉 mv 在覆盖已存在的文件以前请求许可:

  1. #!/bin/sh
  2. if [ -z "$1" ]; then
  3. echo "Provide a \"file name\", using quotes to nullify the space."
  4. exit 1
  5. fi
  6. mv -i "$1" `ls "$1" | tr -d ' '`

如今你的 shell 脚本是有意义的、有用的、友好的 - 你是一个程序员了,因此不要停。学习新命令,在终端中使用它们,记下您的操做,而后编写脚本。最终,你会把本身从工做中解脱出来,当你的机器仆人运行 shell 脚本,接下来的生活将会轻松。

Happy hacking!

 

转 https://linux.cn/article-8219-1.html?utm_source=index&utm_medium=moremore

相关文章
相关标签/搜索