标签 脚本 下的文章

微软爱上 Linux 之后,PowerShell 这个原本只是 Windows 才能使用的组件,于 2016 年 8 月 18 日开源并且成为跨平台软件,登陆了 Linux 和 macOS。

PowerShell 是一个微软开发的自动化任务和配置管理系统。它基于 .NET 框架,由命令行语言解释器(shell)和脚本语言组成。

PowerShell 提供对 COM ( 组件对象模型 Component Object Model ) 和 WMI ( Windows 管理规范 Windows Management Instrumentation ) 的完全访问,从而允许系统管理员在本地或远程 Windows 系统中 执行管理任务,以及对 WS-Management 和 CIM( 公共信息模型 Common Information Model )的访问,实现对远程 Linux 系统和网络设备的管理。

通过这个框架,管理任务基本上由称为 cmdlets(发音 command-lets)的 .NET 类执行。就像 Linux 的 shell 脚本一样,用户可以通过按照一定的规则将一组 cmdlets 写入文件来制作脚本或可执行文件。这些脚本可以用作独立的命令行程序或工具

在 Linux 系统中安装 PowerShell Core 6.0

要在 Linux 中安装 PowerShell Core 6.0,我们将会用到微软软件仓库,它允许我们通过最流行的 Linux 包管理器工具,如 apt-getyum 等来安装。

在 Ubuntu 16.04 中安装

首先,导入该公共仓库的 GPG 密钥,然后将 Microsoft Ubuntu 仓库注册到 APT 的源中来安装 PowerShell

$ curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
$ curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | sudo tee /etc/apt/sources.list.d/microsoft.list
$ sudo apt-get update
$ sudo apt-get install -y powershell

在 Ubuntu 14.04 中安裝

$ curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
$ curl https://packages.microsoft.com/config/ubuntu/14.04/prod.list | sudo tee /etc/apt/sources.list.d/microsoft.list
$ sudo apt-get update
$ sudo apt-get install -y powershell

在 CentOS 7 中安裝

首先,将 Microsoft RedHat 仓库注册到 YUM 包管理器仓库列表中,然后安装 PowerShell

$ sudo curl https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/microsoft.repo
$ sudo yum install -y powershell

如何在 Linux 中使用 PowerShell Core 6.0

在这一节中,我们将会简单介绍下 PowerShell;我们将会看到如何启动 PowerShell,运行一些基础命令,操作文件、目录和进程。然后学习怎样列出所有可用的命令、显示命令帮助和别名。

输入以下命令来启动 PowerShell:

$ powershell

Start Powershell in Linux

在 Linux 中启动 PowerShell

你可以通过以下命令来查看 PowerShell 版本:

$PSVersionTable

Check Powershell Version

查看 PowerShell 版本

在 Linux 中运行基本的 PowerShell 命令。

get-date          [# 显示当前日期]
get-uptime        [# 显示开机时间]
get-location      [# 显示当前工作目录]

在 PowerShell 中操作文件和目录

1、 可以通过两种方法创建空文件:

new-item  tecmint.tex
或者
"">tecmint.tex

然后往里面添加内容并查看文件内容。

set-content tecmint.tex -value "TecMint Linux How Tos Guides"
get-content tecmint.tex

Create New File in Powershell

在 PowerShell 中创建新文件

2、 在 PowerShell 中删除一个文件

remove-item tecmint.tex
get-content tecmint.tex

Delete File in Powershell

在 PowerShell 中删除一个文件

3、 创建目录

mkdir  tecmint-files
cd  tecmint-files
“”>domains.list
ls

Create Directory in Powershell

在 PowerShell 中创建目录

4、 执行长格式的列表操作,列出文件/目录详细情况,包括模式(文件类型)、最后修改时间等,使用以下命令:

dir

Directory Long Listing in Powershell

Powershell 中列出目录长列表

5、 显示系统中所有的进程:

get-process

View Running Processes in Powershell

在 PowerShell 中显示运行中的进程

6、 通过给定的名称查看正在运行的进程/进程组细节,将进程名作为参数传给上面的命令,如下:

get-process apache2

View Specific Process in Powershell

在 PowerShell 中查看指定的进程

输出中各部分的含义:

  • NPM(K) – 进程使用的非分页内存,单位:Kb。
  • PM(K) – 进程使用的可分页内存,单位:Kb。
  • WS(K) – 进程的工作集大小,单位:Kb,工作集由进程所引用到的内存页组成。
  • CPU(s) – 进程在所有处理器上所占用的处理器时间,单位:秒。
  • ID – 进程 ID (PID).
  • ProcessName – 进程名称。

7、 想要了解更多,获取 PowerShell 命令列表:

get-command

List Powershell Commands

列出 PowerShell 的命令

8、 想知道如何使用一个命令,查看它的帮助(类似于 Unix/Linux 中的 man);举个例子,你可以这样获取命令 Describe 的帮助:

get-help Describe

Powershell Help Manual

PowerShell 帮助手册

9、 显示所有命令的别名,輸入:

get-alias

List Powershell Command Aliases

列出 PowerShell 命令别名

10、 最后,不过也很重要,显示命令历史记录(曾运行过的命令的列表):

history

List Powershell Commands History

显示 PowerShell 命令历史记录

就是这些了!在这篇文章里,我们展示了如何在 Linux 中安装微软的 PowerShell Core 6.0。在我看来,与传统 Unix/Linux 的 shell 相比,PowerShell 还有很长的路要走。目前看来,PowerShell 还需要在命令行操作机器,更重要的是,编程(写脚本)等方面,提供更好、更多令人激动和富有成效的特性。

查看 PowerShell 的 GitHub 仓库:https://github.com/PowerShell/PowerShell

请在评论中分享你的观点。


作者简介:

Aaron Kili 是一个 Linux 和 F.O.S.S 狂热爱好者,将来的 Linux 系统管理员、web 开发者,目前是 TecMint 的内容编辑,是一个热爱研究计算机与坚定的分享知识的人。


via: http://www.tecmint.com/install-powershell-in-linux/

作者:Aaron Kili 译者:zijung 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出

我一直很好奇,在启动 Linux 系统并登录的过程中到底发生了什么事情。按下开机键或启动一个虚拟机,你就启动了一系列事件,之后会进入到一个功能完备的系统中,有时,这个过程不到一分钟。当你注销或者关机时,也是这样。

更有意思的是,在系统启动以及用户登录或注销时,还可以让系统执行特定的操作。

本文,我们将探讨一下在 Linux 操作系统中实现这些目标的传统方法。

注意:我们假定使用的是 Bash 作为登录及注销的主 Shell。如果你使用的是其他 Shell,那么有些方法可能会无效。如果有其他的疑问,请参考对应的 Shell 文档。

在启动时执行 Linux 脚本

有两种传统的方法可以实现在启动时执行命令或脚本:

方法 #1 - 使用 cron 任务

除了常用格式(分 / 时 / 日 / 月 / 周)外,cron 调度器还支持 @reboot 指令。这个指令后面的参数是脚本(启动时要执行的那个脚本)的绝对路径。

然而,这种方法需要注意两点:

  • a) cron 守护进程必须处于运行状态(通常情况下都会运行),同时
  • b) 脚本或 crontab 文件必须包含需要的环境变量(如果有的话,参考 StackOverflow 获取更多详细内容)。

方法 #2 - 使用 /etc/rc.d/rc.local

这个方法对于 systemd-based 发行版 Linux 同样有效。不过,使用这个方法,需要授予 /etc/rc.d/rc.local 文件执行权限:

# chmod +x /etc/rc.d/rc.local

然后在这个文件底部添加脚本。

下图说明如何分别使用 cron 任务和 rc.local 运行两个示例脚本(/home/gacanepa/script1.sh/home/gacanepa/script2.sh)。

script1.sh:

#!/bin/bash
DATE=$(date +'%F %H:%M:%S')
DIR=/home/gacanepa
echo "Current date and time: $DATE" > $DIR/file1.txt

script2.sh:

#!/bin/bash
SITE="Tecmint.com"
DIR=/home/gacanepa
echo "$SITE rocks... add us to your bookmarks." > $DIR/file2.txt

启动时执行 Linux 脚本

*启动时执行 Linux 脚本 *

记住,一定要提前给两个示例脚本授予执行权限:

$ chmod +x /home/gacanepa/script1.sh
$ chmod +x /home/gacanepa/script2.sh

在登录或注销时执行 Linux 脚本

要在登录或注销时执行脚本,分别需要使用 ~.bash_profile~.bash_logout 文件。多数情况下,后者需要手动创建。在每个文件的底部,添加调用脚本代码,如前面例中所示,就可以实现这个功能。

总结

本文主要介绍如何在启动、登录以及注销系统时执行脚本。如果你有其他的方法可以补充,请使用下面的评论表给我们指出,我们期待您的回应!


作者简介:

Gabriel Cánepa 是 GNU/Linux 系统管理员, 阿根廷圣路易斯 Villa Mercedes 的 web 开发人员。他为一家国际大型消费品公司工作,在日常工作中使用 FOSS 工具以提高生产力,并从中获得极大乐趣。


via: http://www.tecmint.com/auto-execute-linux-scripts-during-reboot-or-startup/

作者:Gabriel Cánepa 译者:zhb127 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出

 title=

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

Kernighan 的话来说:“UNIX 系统程序基本上是 …… 你可以用来创造东西的构件。…… 管道的概念是 [UNIX] 系统的基础;你可以拿一堆程序 …… 并将它们端到端连接到一起,使数据从左边的一个流到右边的一个,由系统本身管着所有的连接。程序本身不知道任何关于连接的事情;对它们而言,它们只是在与终端对话。”

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

POSIX 操作系统本身就像是一个 API。如果你能弄清楚如何在 POSIX 的 shell 中完成一个任务,那么你可以自动化这个任务。这就是编程,这种日常 POSIX 编程方法的主要方式就是 shell 脚本。

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

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

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

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

Hello world

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

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

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

像应用程序一样运行脚本:

$ chmod +x hello.sh
$ ./hello.sh
hello world

不管多少,这就是一个 shell 脚本了。

现在让我们处理一些有用的东西。

去除空格

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

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

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

$ touch foo\ bar.txt
$ ls "foo bar.txt"
foo bar.txt

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

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

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

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

despace is a shell script for removing spaces from file names.

Usage:
$ despace "foo bar.txt"

现在让我们弄明白如何手动做,并且如何去构建脚本。

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

$ ls
hello.sh
foo bar.txt

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

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

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

现在你得到了所需的输出了。

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

$ NAME=foo

当您需要返回信息时,可以通过在变量名称前面缀上美元符号($ )来引用该位置。

$ echo $NAME
foo

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

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

我们完成了一半的目标,现在可以从源文件名确定目标文件名了。

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

#!/bin/sh

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

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

$ mv "foo bar.txt" foobar.txt

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

#!/bin/sh

NAME=`ls "foo bar.txt" | tr -d ' '`
echo $NAME
mv "foo bar.txt" $NAME

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

$ touch "foo bar.txt"
$ chmod +x despace
$ ./despace
foobar.txt
$ ls
foobar.txt

去除空格 v2.0

脚本可以正常工作,但不完全如您的文档所述。它目前非常具体,只适用于一个名为 foo\ bar.txt 的文件,其它都不适用。

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

#!/bin/sh

NAME=`ls $1 | tr -d ' '`
echo $NAME
mv $1 $NAME

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

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

然后测试你的新脚本:

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

看起来您发现了一个 bug!

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

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

#!/bin/sh

NAME=`ls "$1" | tr -d ' '`
echo $NAME
mv "$1" $NAME

再做个测试:

$ ./despace "one two.txt"
onetwo.txt
$ ./despace c*g.txt
catdog.txt

此脚本的行为与任何其它 POSIX 命令相同。您可以将其与其他命令结合使用,就像您希望的使用的任何 POSIX 程序一样。您可以将其与命令结合使用:

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

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

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

等等。

去除空格 v2.5

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

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

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

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

会变换成:

$ mv foo\ bar.txt foobar.txt

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

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

#!/bin/sh

mv "$1" `ls "$1" | tr -d ' '`

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

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

$ ./despace
ls: cannot access '': No such file or directory

mv: missing destination file operand after ''
Try 'mv --help' for more information.

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

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

if 和 test

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

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

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

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

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

#!/bin/sh

if [ -z "$1" ]; then
   echo "Provide a \"file name\", using quotes to nullify the space."
   exit 1
fi

mv "$1" `ls "$1" | tr -d ' '`

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

尝试一下:

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

成功!

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

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

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

#!/bin/sh

if [ -z "$1" ]; then
   echo "Provide a \"file name\", using quotes to nullify the space."
   exit 1
fi

mv -i "$1" `ls "$1" | tr -d ' '`

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

Happy hacking!


作者简介:

Seth Kenlon 是一位独立的多媒体艺术家,自由文化倡导者和 UNIX 极客。他是基于 Slackware 的多媒体制作项目(http://slackermedia.ml)的维护者之一


via: https://opensource.com/article/17/1/getting-started-shell-scripting

作者:Seth Kenlon 译者:hkurj 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

shell 脚本调试系列 中,本文将解释第三种 shell 脚本调试模式,即 shell 跟踪,并查看一些示例来演示它如何工作以及如何使用它。

本系列的前面部分清晰地阐明了另外两种 shell 脚本调试模式:详细模式和语法检查模式,并用易于理解的例子展示了如何在这些模式下启用 shell 脚本调试。

  1. 如何在 Linux 中启用 Shell 脚本的调试模式
  2. 如何在 Shell 脚本中执行语法检查调试模式

shell 跟踪简单的来说就是跟踪 shell 脚本中的命令的执行。要打开 shell 跟踪,请使用 -x 调试选项。

这会让 shell 在终端上显示所有执行的命令及其参数。

我们将使用下面的 sys_info.sh shell 脚本,它会简要地打印出你的系统日期和时间、登录的用户数和系统的运行时间。不过,脚本中包含我们需要查找和更正的语法错误。

#!/bin/bash
# script to print brief system info
ROOT_ID="0"
DATE=`date`
NO_USERS=`who | wc -l`
UPTIME=`uptime`
check_root(){
  if [ "$UID" -ne "$ROOT_ID" ]; then
    echo "You are not allowed to execute this program!"
    exit 1;    
}
print_sys_info(){
  echo "System Time    : $DATE"
  echo "Number of users: $NO_USERS"
  echo "System Uptime  : $UPTIME
}
check_root
print_sys_info
exit 0

保存文件并执行脚本。脚本只能用 root 用户运行,因此如下使用 sudo 命令运行:

$ chmod +x sys_info.sh
$ sudo bash -x sys_info.sh

Shell Tracing - Show Error in Script

shell 跟踪 - 显示脚本中的错误

从上面的输出我们可以观察到,首先执行命令,然后其输出做为一个变量的值。

例如,先执行 date,其输出做为变量 DATE 的值。

我们可以执行语法检查来只显示其中的语法错误,如下所示:

$ sudo bash -n sys_info.sh 

Syntax Checking in Script

脚本中语法检查

如果我们审视这个 shell 脚本,我们就会发现 if 语句缺少了封闭条件的 fi 关键字。因此,让我们加上它,新的脚本应该看起来像这样:

#!/bin/bash
#script to print brief system info
ROOT_ID="0"
DATE=`date`
NO_USERS=`who | wc -l`
UPTIME=`uptime`
check_root(){
  if [ "$UID" -ne "$ROOT_ID" ]; then
    echo "You are not allowed to execute this program!"
    exit 1;
  fi    
}
print_sys_info(){
  echo "System Time    : $DATE" 
  echo "Number of users: $NO_USERS"
  echo "System Uptime  : $UPTIME
}
check_root
print_sys_info
exit 0

再次保存文件并以 root 执行,同时做语法检查:

$ sudo bash -n sys_info.sh

Perform Syntax Check in Shell Scripts

在 shell 脚本中执行语法检查

上面的语法检查操作的结果仍然显示在脚本的第 21 行还有一个错误。所以,我们仍然要纠正一些语法。

再一次分析脚本,会发现第 21 行的错误是由于在 print_sys_info 函数内最后一个 echo 命令中没有闭合双引号 "

我们将在 echo 命令中添加闭合双引号并保存文件。修改过的脚本如下:

#!/bin/bash
#script to print brief system info
ROOT_ID="0"
DATE=`date`
NO_USERS=`who | wc -l`
UPTIME=`uptime`
check_root(){
  if [ "$UID" -ne "$ROOT_ID" ]; then
    echo "You are not allowed to execute this program!"
    exit 1;
  fi
}
print_sys_info(){
  echo "System Time    : $DATE"
  echo "Number of users: $NO_USERS"
  echo "System Uptime  : $UPTIME"
}
check_root
print_sys_info
exit 0

现在再一次检查语法。

$ sudo bash -n sys_info.sh

上面的命令不会产生任何输出,因为我们的脚本语法上正确。我们也可以再次跟踪脚本执行,它应该工作得很好:

$ sudo bash -x sys_info.sh

Trace Shell Script Execution

跟踪 shell 脚本执行

现在运行脚本。

$ sudo ./sys_info.sh

Shell Script to Show Date, Time and Uptime

用 shell 脚本显示日期、时间和运行时间

shell 跟踪执行的重要性

shell 脚本跟踪可以帮助我们识别语法错误,更重要的是识别逻辑错误。例如,在 sys_info.sh shell 脚本中的 check_root 函数,它用于确定用户是否为 root,因为脚本只允许由超级用户执行。

check_root(){
  if [ "$UID" -ne "$ROOT_ID" ]; then
    echo "You are not allowed to execute this program!"
    exit 1;
  fi
}

这里的魔法是由 if 语句表达式 ["$ UID" -ne "$ ROOT_ID"] 控制的,一旦我们不使用合适的数字运算符(示例中为 -ne,这意味着不相等),我们最终可能会出一个逻辑错误。

假设我们使用 -eq (意思是等于),这将允许任何系统用户以及 root 用户运行脚本,因此是一个逻辑错误。

check_root(){
  if [ "$UID" -eq "$ROOT_ID" ]; then
    echo "You are not allowed to execute this program!"
    exit 1;
  fi
}

注意:我们在本系列开头介绍过,set 这个 shell 内置命令可以在 shell 脚本的特定部分激活调试。

因此,下面的行将帮助我们通过跟踪脚本的执行在其中找到这个逻辑错误:

具有逻辑错误的脚本:

#!/bin/bash
#script to print brief system info
ROOT_ID="0"
DATE=`date`
NO_USERS=`who | wc -l`
UPTIME=`uptime`
check_root(){
  if [ "$UID" -eq "$ROOT_ID" ]; then
    echo "You are not allowed to execute this program!"
    exit 1;
  fi
}
print_sys_info(){
  echo "System Time    : $DATE"
  echo "Number of users: $NO_USERS"
  echo "System Uptime  : $UPTIME"
}
#turning on and off debugging of check_root function
set -x ; check_root;  set +x ;
print_sys_info
exit 0

保存文件并调用脚本,在输出中,我们可以看到一个普通系统用户可以在未 sudo 的情况下运行脚本。 这是因为 USER_ID 的值为 100,不等于为 0 的 root 的 ROOT_ID

$ ./sys_info.sh

Run Shell Script Without Sudo

未 sudo 的情况下运行 shell 脚本

那么,现在我们已经完成了 shell 脚本调试系列,可以在下面的反馈栏里给我们关于本篇或者本系列提出问题或反馈。


作者简介:

Aaron Kili 是 Linux 和 F.O.S.S 爱好者,将来的 Linux SysAdmin、web 开 发人员,目前是 TecMint 的内容创作者,他喜欢用电脑工作,并坚信分享知识。


via: http://www.tecmint.com/trace-shell-script-execution-in-linux/

作者:Aaron Kili 译者:geekpi 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出

我们开启了 Shell 脚本调试系列文章,先是解释了不同的调试选项,下面介绍如何启用 Shell 调试模式

写完脚本后,建议在运行脚本之前先检查脚本中的语法,而不是查看它们的输出以确认它们是否正常工作。

在本系列的这一部分,我们将了解如何使用语法检查调试模式。记住我们之前在本系列的第一部分中解释了不同的调试选项,在这里,我们将使用它们来执行脚本调试。

启用 verbose 调试模式

在进入本指导的重点之前,让我们简要地探索下 verbose 模式。它可以用 -v 调试选项来启用,它会告诉 shell 在读取时显示每行。

要展示这个如何工作,下面是一个示例脚本来批量将 PNG 图片转换成 JPG 格式

将下面内容输入(或者复制粘贴)到一个文件中。

#!/bin/bash
#convert
for image in *.png; do
convert  "$image"  "${image%.png}.jpg"
echo "image $image converted to ${image%.png}.jpg"
done
exit 0

接着保存文件,并用下面的命令使脚本可执行:

$ chmod +x script.sh

我们可以执行脚本并显示它被 Shell 读取到的每一行:

$ bash -v script.sh

Display All Lines in Shell Script

显示shell脚本中的所有行

在 Shell 脚本中启用语法检查调试模式

回到我们主题的重点,-n 激活语法检查模式。它会让 shell 读取所有的命令,但是不会执行它们,它(shell)只会检查语法。

一旦 shell 脚本中发现有错误,shell 会在终端中输出错误,不然就不会显示任何东西。

激活语法检查的命令如下:

$ bash -n script.sh

因为脚本中的语法是正确的,上面的命令不会显示任何东西。所以,让我们尝试删除结束 for 循环的 done 来看下是否会显示错误:

下面是修改过的含有 bug 的批量将 png 图片转换成 jpg 格式的脚本。

#!/bin/bash
#script with a bug
#convert
for image in *.png; do
convert  "$image"  "${image%.png}.jpg"
echo "image $image converted to ${image%.png}.jpg"
exit 0

保存文件,接着运行该脚本并执行语法检查:

$ bash -n script.sh

Check Syntax in Shell Script

检查 shell 脚本语法

从上面的输出中,我们看到我们的脚本中有一个错误,for 循环缺少了一个结束的 done 关键字。shell 脚本从头到尾检查文件,一旦没有找到它(done),shell 会打印出一个语法错误:

script.sh: line 11: syntax error: unexpected end of file

我们可以同时结合 verbose 模式和语法检查模式:

$ bash -vn script.sh

Enable Verbose and Syntax Checking in Script

在脚本中同时启用 verbose 检查和语法检查

另外,我们可以通过修改脚本的首行来启用脚本检查,如下面的例子:

#!/bin/bash -n
#altering the first line of a script to enable syntax checking
#convert
for image in *.png; do
convert  "$image"  "${image%.png}.jpg"
echo "image $image converted to ${image%.png}.jpg"
exit 0

如上所示,保存文件并在运行中检查语法:

$ ./script.sh
script.sh: line 12: syntax error: unexpected end of file

此外,我们可以用内置的 set 命令来在脚本中启用调试模式。

下面的例子中,我们只检查脚本中的 for 循环语法。

#!/bin/bash
#using set shell built-in command to enable debugging
#convert
#enable debugging
set -n
for image in *.png; do
convert  "$image"  "${image%.png}.jpg"
echo "image $image converted to ${image%.png}.jpg"
#disable debugging
set +n
exit 0

再一次保存并执行脚本:

$ ./script.sh 

总的来说,我们应该保证在执行 Shell 脚本之前先检查脚本语法以捕捉错误。

请在下面的反馈栏中,给我们发送关于这篇指导的任何问题或反馈。在这个系列的第三部分中,我们会解释并使用 shell 追踪调试模式。


作者简介:

Aaron Kili 是一个 Linux 及 F.O.S.S 热衷者,即将是 Linux 系统管理员、web 开发者,目前是 TecMint 的内容创作者,他喜欢用电脑工作,并热心分享知识。


via: http://www.tecmint.com/check-syntax-in-shell-script/

作者:Aaron Kili 译者:geekpi 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出

脚本是存储在一个文件的一系列命令。在终端上输入一个个命令,按顺序执行的方法太弱了,使用脚本,系统中的用户可以在一个文件中存储所有命令,反复调用该文件多次重新执行命令。

在学习脚本或写脚本的初期阶段,我们通常从写小脚本或者几行命令的短脚本开始,调试这样的脚本时我们通常无非就是通过观察它们的输出来确保其正常工作。

然而,当我们开始写非常长或上千行命令的高级脚本,例如改变系统设置的脚本,在网络上执行关键备份 等等,我们会意识到仅仅看脚本输出是不足以在脚本中找到 Bug 的!

因此,在 Linux 系列中这篇介绍 Shell 脚本调试, 我们将看看如何启用 Shell 脚本调试,然后在之后的系列中解释不同的 Shell 脚本调试模式以及如何使用它们。

如何开始写一个脚本

一个脚本与其它文件的区别是它的首行,它包含 #! (She-Bang - 释伴:定义文件类型)和路径名(解释器路径),通知系统该文件是一个命令集合,将被指定程序(解释器)解释。

下面是不同类型脚本 首行 示例:

#!/bin/sh          [sh 脚本]
#!/bin/bash        [bash 脚本] 
#!/usr/bin/perl    [perl 程序]
#!/bin/awk -f      [awk 脚本]   

注意:如果脚本仅包含一组标准系统命令,没有任何内部 Shell 指令,首行或 #! 可以去掉。

如何在 Linux 操作系统执行 Shell 脚本

调用一个脚本脚本的常规语法是:

$ 脚本名  参数1 ... 参数N

另一种可能的形式是明确指定将执行这个脚本的 Shell,如下:

$ shell 脚本名  参数1 ... 参数N

示例:

$ /bin/bash   参数1 ... 参数N     [bash 脚本]
$ /bin/ksh   参数1 ... 参数N      [ksh 脚本]
$ /bin/sh   参数1 ... 参数N       [sh 脚本]

对于没有 #! 作为首行,仅包含基础系统命令的脚本,示例如下:

### 脚本仅包含标准系统命令
cd /home/$USER
mkdir tmp
echo "tmp directory created under /home/$USER"

使它可执行并运行,如下:

$ chmod +x  脚本名
$ ./脚本名 

启用 Shell 脚本调试模式的方法

下面是主要的 Shell 脚本调试选项:

  • -v (verbose 的简称) - 告诉 Shell 读取脚本时显示所有行,激活详细模式。
  • -n (noexec 或 no ecxecution 简称) - 指示 Shell 读取所有命令然而不执行它们,这个选项激活语法检查模式。
  • -x (xtrace 或 execution trace 简称) - 告诉 Shell 在终端显示所有执行的命令和它们的参数。 这个选项是启用 Shell 跟踪模式。

1、 改变 Shell 脚本首行

第一个机制是改变 Shell 脚本首行,如下,这会启动脚本调试。

#!/bin/sh 选项

其中, 选项可以是上面提到的一个或多个调试选项。

2、 调用 Shell 调试选项

第二个是使用如下调试选项启动 Shell,这个方法也会打开整个脚本调试。

$ shell 选项   参数1 ... 参数N

示例:

$ /bin/bash 选项   参数1 ... 参数N

3、 使用 Shell 内置命令 set

第三个方法是使用内置命令 set 去调试一个给定的 Shell 脚本部分,如一个函数。这个机制是重要的,因为它让我们可以去调试任何一段 Shell 脚本。

我们可以如下使用 set 命令打开调试模式,其中选项是之前提到的所有调试选项。

$ set 选项 

启用调试模式:

$ set -选项

禁用调试模式:

$ set +选项

此外,如果我们在 Shell 脚本不同部分启用了几个调试模式,我们可以一次禁用所有调试模式,如下:

$ set -

关于启用 Shell 脚本调试模式,先讲这些。正如我们看到的,我们可以调试一整个 Shell 脚本或者特定部分脚本。

在此系列下面的两篇文章中,我们会举例介绍如何使用 Shell 脚本调试选项,进一步了解 详细 verbose 语法检查 syntax checking 跟踪 tracing 调试模式。

更重要的是,关于这个指南,欢迎通过下面评论提出任何问题或反馈。


via: http://www.tecmint.com/enable-shell-debug-mode-linux/

作者:Aaron Kili 译者:imxieke 校对:jasminepeng

本文由 LCTT 组织编译,Linux中国 荣誉推出