windows外壳_一些漂亮的外壳技巧

在你开始前

了解对本教程的期望以及如何最大程度地利用本教程。

关于本系列

系列教程简要介绍了UNIX®基本概念,并针对新用户的角度进行了编写。 该系列中的前三篇教程提供了针对MicrosoftWindows®背景的新用户的UNIX系统概述,描述了文件系统和常用命令,介绍了vi (最广泛使用的UNIX编辑器),以及使用grepsedawk工具快速了解过滤器和正则表达式。

关于本教程

本教程提供了一些壳技巧和技巧的集合,这些技巧和技巧对于新用户来说很方便。 它显示了如何使用Bourne Shell中编写的小脚本自动执行特殊情况,包括自动基本转换,读取键盘输入,在子Shell中执行命令,对目录中所有文件执行命令以及各种形式的循环。 本教程以一组有用的外壳一线结束。

目标

本教程的目的是向新用户展示如何使用和实现许多Shell方法来提供不同级别的自动化。 它通过提供特殊情况的技巧和技巧来演示这些方法,并且还提供了用于常见任务的有用的外壳单层衬套。

先决条件

本教程是为UNIX较新的用户编写的。 唯一的先决条件是UNIX文件系统的基本知识以及操作该文件的命令,命令行本身以及使用诸如vi类的编辑器来编辑文本文件。 所有这些概念在本系列以前的教程中都有详细介绍。

系统要求

您需要使用兼容Bourne的shell环境(例如流行的bash shell)对UNIX系统进行用户级访问。 这是本教程的唯一系统要求。

Shell命令执行

学习shell脚本的最佳方法是通过示例。 您将在脚本中执行的任何命令都可以在命令行中直接尝试,这就是本教程中提供的许多动手示例。 例如, echo命令将一行文本写入标准输出。 (许多外壳程序提供它们自己的echo版本作为内置命令,包括Bourne shell的IBMAIX®实现。如果您是这种情况,那么在运行echo ,实际上是外壳程序正在运行的命令版本。 )

报价单

尝试通过将短消息括在引号中来输出带有echo的短消息:

$ echo "Hello, world"
Hello, world

无论是在命令行还是在脚本中,Shell 引号都是将字符串传递给Shell的一种方式,这样字符串就可能不包含任何特殊的元字符。 当字符串包含多个单词时,可以使用引号并将一个包含空格的短语括起来。 当一个字符正好是一个外壳元字符并且想要取消使用它的特殊含义时(例如,当您希望将美元符号( $ )作为字面的美元符号字符而不是前面的特殊元字符传递时,您也要引用单个字符)变量名。

引用文本内会发生各种扩展 。 例如,在双引号文本内部,变量将扩展为它们的值,而在单引号文本内部进行引用时,文字变量名称不会扩展。

需要了解三种重要的报价类型:

  1. 在单个字符前加反斜杠(\)来引用该字符。 这将传递文字字符,而不传递其可能具有的任何特殊含义,例如空格字符或shell元字符。 例如,要引用星号(*)(它是一个外壳元字符),请使用\* 。 要引用实际的反斜杠字符,请使用\\

  2. 通过将文本字符串括在双引号( " )中来传递扩展的引号 。美元符号( $ )和单引号( ' )字符保持其含义。因此,除其他外,引号中引用的任何变量名都将被替换。换行之前的反斜杠或某些字符( $`"\ )将被删除,但它们引用的字符将被传递。

  3. 通过将文本括在单引号( ' )中,传递文本字符串的文字引号 (传递所有变量名,元字符等作为字符本身,而不传递其含义或值)。

请注意,不同的外壳使用不同的报价规则。 请查阅您特定外壳的man页,以了解其确切规则。

尝试分配一个变量,然后以各种引号样式输出它,如清单1所示。

清单1.用echo演示shell变量引用样式
$ myvar = "Hello, world"
$ echo $myvar
Hello, world
$ echo "$myvar"
Hello, world
$ echo '$myvar'
$myvar
$ echo \$myvar
$myvar
$ echo \'$myvar\'
'Hello, world'
$ echo "'$myvar'"
'Hello, world'
$ echo '"$myvar"'
"$myvar"
$ echo \"$myvar\"
"Hello, world"

请注意,根据所使用的引用样式,如何用不同的方式解释变量。

注释

在外壳程序中,井号( # )开始注释行。 散列及其后面的所有内容都将被忽略。 尝试键入插入注释的行,如清单2所示:

清单2.在shell中使用注释
$ # a comment does nothing
$ echo "Hello, world" # This text is ignored
Hello, world
$ echo # This will not output

$ echo 'But a hash (#) can be quoted'
But a hash (#) can be quoted
$ echo "# Even in double quotes"
# Even in double quotes
$

制作一个shell脚本

如您所见,您可以直接在命令行上测试这些Shell编程结构。 但是,当您毕业于单行命令之外而实际上开始编写更长的程序时,则需要将它们写入称为脚本的文件中。 脚本是一个文本文件,具有可执行位,并且包含一个由外壳语言的命令组成的程序。 UNIX shell是一种解释语言 ,这意味着它的程序不是编译的而是由解释器读取的,解释器是shell可执行文件本身,例如/bin/sh/bin/bsh/bin/bash

Shell脚本的第一行始终是相同的:

#!/bin/sh

这是外壳程序自己用来确定文件语言或内容的特殊注释行。 感叹号(在UNIX中通常称为bang和排版习惯用语),后跟路径名指示外壳程序应使用解释器来执行文件。 在这种情况下,它是/bin/sh ,在许多系统上,它是Bourne shell可执行文件本身。 例如,专门为Korn shell编写的脚本可能以#!/usr/bin/ksh行开头,就像Ruby脚本以#!/usr/bin/ruby开头一样。 安装bash/bin/sh通常是指向bash二进制文件的符号链接。 并且,出于兼容性考虑,使用/bin/sh比使用/bin/bash更可取。 在某些系统上,例如IBM AIX 5L™,Bourne Shell可执行文件的名称为bsh ,位于/ usr / bin / bsh中。

清单3给出了一个简短的示例shell脚本。

清单3.示例shell脚本
#!/bin/sh
# This is a shell script
message = "Hello, world!"
echo "The message is '"$message"'"

使用vi进行键入,并将其保存到名为myscript的文件中,如本系列先前的教程中所述(请参阅参考资料 )。 然后,使用chmod通过在文件上设置执行许可权使其可执行:

$ chmod u+x myscript

此命令使它只能由您执行。 如果要让系统上的所有用户运行它,则可以始终为所有用户设置执行权限:

$ chmod a+x myscript

现在您可以运行它。 给出与当前工作目录相关的文件名,该文件名在路径中以点字符( . )指定:

$ ./myscript
The message is 'Hello, world!'
$

shell变量PATH包含由冒号分隔的目录列表。 据说这是您的路径 ,并且外壳程序始终“查看”这些目录中的任何文件。 UNIX路径的目的是使运行二进制文件变得很方便。 这就是为什么您只键入命令的基本文件名,例如lsecho ,而不是给出它们的完整或相对路径名的原因。 如果将脚本移动到路径上的目录,则只需键入其名称即可运行它。 确切的路径取决于您的UNIX实现和本地设置,但是通常该路径上的目录包括/ bin,/ usr / bin和/ usr / local / bin。

一些用户配置其shell,以便PATH变量包含当前工作目录,该目录在路径中以点字符(“ . ”)指定。 这样,要在当前目录中运行脚本,只需键入其名称,而无需指定相对目录。 Shell按照给定的顺序沿着路径搜索; 因此,为了防止特洛伊木马或意外事故,将当前工作目录放在路径末尾的任何位置都是不明智的。

要查看路径,请使用echo来显示PATH变量的内容,如清单4所示。

清单4.更改PATH
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11
$ myscript
myscript: command not found
$ PATH = $PATH":."
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:.
$ myscript
The message is 'Hello, world!'
$

特殊选项或标志可以跟随解释器的名称,例如/usr/bin/bsh -n ,用于调试目的。 连字符可以关闭选项,而加号可以打开选项。 特殊的内置环境变量- (连字符)包含当前shell的选项的完整列表。

尝试显示在当前的交互式外壳程序中设置了哪些选项。 通过使用echo显示-变量的内容来执行此操作:

$ echo $-
himBH
$

请查阅您的Shell man页以获取标志和选项的最新列表。 表1列出了AIX®上Bourne shell的通用标志,以及每个标志的简要说明。

表1. AIX Bourne shell的常用选项
描述
-a 导出所有分配了值的变量。
-c Variable 执行从Variable读取的命令。
-e 如果命令满足以下条件之一,则立即退出:该命令以大于0的返回值退出; 该命令不是while一部分, until ,或者if构造; 该命令未被ANDOR测试; 或者该命令不是以bang开头的管道。
-f 禁用所有文件名替换。
-h 找到并记住定义函数时在函数内调用的所有命令。
-i 指定一个交互式外壳。
-k 将所有关键字放在命令环境中。
-n 读取命令,但不要执行它们。
-r 调用受限外壳。
-s 从标准输入读取命令,并将输出写入标准错误(不包括Shell内置输出)。
-t 读取并执行一个命令,然后退出。
-u 在脚本中,将所有未设置的变量视为错误。 尝试变量替换时退出。
-v 显示正在读取的输入行。
-x 在执行完整命令(所有参数和选项)之前,请先显示它们。

Shell算术和基数转换

Shell提供了许多基本的算术运算,这些运算在脚本中很有用。 外壳程序对您提供的算术表达式进行求值,然后执行算术扩展 ,然后用结果替换该表达式。 以这种形式给出算术表达式:

$(( expression )

通过使用echo在命令行上显示结果,您可以看到正在工作的算术扩展。 现在尝试清单5中显示的内容。

清单5. Bourne shell中的算术扩展
$ echo $((10+40))
50
$ echo $((5*(3+3)))
30

您还可以将扩展分配给变量。 尝试清单6中显示的内容

清单6.为shell变量分配算术扩展
$ myvar = 10
$ echo $myvar
10
$ echo $(($myvar-2))
8
$ myvar = $(($myvar+5))
$ echo $myvar
15
$ result = $(($myvar-10))
$ echo $result
5
$

表2列出了在大多数Bourne和Bourne兼容shell中的表达式之间可能使用的一些有效运算符。 与上面的第二个示例一样,分组在其自己的括号中的语句具有优先权。 实际上,shell算术优先级通常是根据C语言的规则确定的。

表2. Shell条件
操作员 描述
+ 加成
- 减法
* 乘法
/
%
< 小于(如果为true,则为1,如果为false,则为0)
<= 小于或等于(如果为true,则为1;如果为false,则为0)
> 大于(如果为true,则为1,如果为false,则为0)
>= 大于或等于(如果为true,则为1;如果为false,则为0)
<< 左移:将第一个表达式的给定整数向左移动第二个表达式的位数
>> 右移:将第一个表达式的给定整数向右移动第二个表达式的位数

使用Shell算术进行基数转换

假设您有一些数字,但是在脚本中,您需要在另一个基础上进行处理。 使用Shell算术可以轻松自动完成此转换。 一种方法是使用Shell算术将数字从给定的底数转换为十进制。 如果数字以算术扩展形式给出,则除非以零开头( 否则假定为八进制或0x), 否则将假定为十进制表示形式。十六进制。 键入以下命令以获取某些八进制和十六进制值的十进制输出:

$ echo $((013))
$ echo $((0xA4))

您还可以使用以下格式指定2到64之间的任意基数:

$((BASE#NUMBER))

尝试通过在shell提示符下键入清单7中所示的行, 二进制,八进制,十六进制和其他基数的数字转换为十进制。

清单7.在shell中以任意基数输出数字
echo $((2#1101010))
echo $((8#377))
echo $((16#D8))
echo $((12#10))
echo $((36#ZZYY))

使用bc基本转换

在shell中进行基本转换的另一个技巧是使用bc (任意精度计算器语言),该语言在大多数UNIX安装中都可用。 因为它允许您指定输出基数,所以当您需要使用非十进制的输出时,这是一种很好的技术。

特殊的bc变量ibaseobase包含用于输入和输出的基准值。 默认情况下,两者都设置为10。要执行基本转换,请转换它们中的一个或两个,然后给它一个数字。 现在尝试一下,如清单8所示。

清单8.使用bc执行基本转换
$ bc -ql10
10
obase=1610
A
ibase=210
2

$

要进行快速的基数转换,请将bcecho结合使用以进行快速的bc转换,将给定值传递给bc 。 输入清单9中显示的内容

清单9. bc的Shell一线
$ echo 'obase=16; 47' | bc
2F
$ echo 'obase=10; ibase=16; A03' | bc
2563
$

注意事项:bc设置输入基数之后,输入到bc所有数字都将以该基数为单位,包括您提供的用于设置输出基数的任何数字。 因此,最好先设置输出基准,否则可能会得到意外的结果,如清单10所示。

清单10.设置输入和输出库时,优先级很重要
$ echo 'ibase=16; obase=10; A' | bc
A
$ echo 'ibase=16; obase=A; A' | bc
10
$

内联输入

虽然echo技巧可通过管道传递给交互式命令(例如bc ,从而在命令行中提供快速的单行代码,但对于多行输入(例如,当可能使用实际文件的内容时)并不实用。 但是还有另一种有用的方法。 该外壳程序具有一种称为here document或内联输入的功能,这是一种动态构建文件的好方法,例如在脚本内部,并将该文件的内容重定向到命令。

使用shell <<运算符指定一个here文档,并在其后一行加上一个限制字符串 ,该字符串是标记输入终止的字符串,可以是您选择的任何文本,只要它是一个没有空格字符的单词。 紧随其后的是构成输入文件的输入行,并在其自己的行上以限制字符串终止输入-它之前或之后不能包含任何文本,或者该行被认为是其中的一部分输入。 与cat一起尝试,如清单11所示。

清单11.制作一个here文档
$ cat << END
>  END of input text
> END
> This is still not the END
> ENDING SOON
> THE END
> END
 END of input text
END 
This is still not the END
ENDING SOON
THE END
$

限制字符串(在这种情况下为END )可以出现在输入中的任何位置-仅当它出现在自己的行中且没有空格或其他字符时,它才用作输入的终止。

脚本中的内联输入

内联输入通常在脚本中使用,以将使用信息输出到标准输出。 通常通过将here文档发送到cat ,如清单12中的脚本所示 。 使用vi进行键入,然后将其保存到名为baseconv的文件中,并使该文件可执行(请参见制作shell脚本部分)。

清单12.使用here文档提供shell脚本用法信息
#!/bin/sh
cat << EOF
baseconv is a program to convert a number from one base to another.

Usage: baseconv [options]

Options:

-i	BASE input base
-o	BASE output base
-h	display this message

For more information, consult the baseconv man page.
EOF

执行脚本后,将here文档的内容发送到标准输出(使用cat )。 现在尝试一下,如清单13所示。

清单13.来自here文档的shell脚本用法信息的输出
$ baseconv
baseconv is a program to convert a number from one base to another.

Usage: baseconv [options]

Options:

-i	BASE input base
-o	BASE output base
-h	display this message

For more information, consult the baseconv man page.
$

此外,Bourne Shell的大多数实现都可以识别使用可选的连字符重定向的内联输入。 可选的连字符可从所有输入行的开头以及包含限制字符串本身的行中删除所有前导制表符。 在编写要保留当前缩进的脚本时,这很有用。 因为内联输入通常是从字面上获取的,并且必须在行的开头给出限制字符串,所以输入将破坏当前的缩进并使脚本看起来不美观。 因此,您可以重写清单12中的脚本以匹配清单14 ,并且输出将是相同的。

清单14.此处的shell脚本记录了前导缩进
#!/bin/sh

	cat <<- EOF
		baseconv is a program to convert a number from one base to another.

		Usage: baseconv [options]

		Options:

		-i	BASE input base
		-o	BASE output base
		-h	display this message

		For more information, consult the baseconv man page.
		EOF

在命令行中内联输入

在命令行中,内联输入与调用交互式程序的单行代码一起使用,例如在使用bc进行基础转换部分中讨论的bc计算器。 您可以使用here文档替代任何交互式命令的真实文件或真实输入的任意行。

尝试使用here文档将多行输入发送到bc 。 输入清单15中显示的内容

清单15.将内联输入发送到交互式程序
$ bc << EOF
> ibase=16
> A
> EOF
10
$

通常使用内联输入扩展变量。 尝试清单16中显示的内容

清单16.内联输入中变量扩展的发生方式
$ BASECON=16
$ bc << EOF
> ibase=16
> $BASECON
> EOF
22
$

子shell执行

可以在称为subshel​​l的新shell中执行命令或命令列表,其父级是当前shell。 子外壳采用其父级的环境。 I / O重定向可以在子外壳和父外壳之间发生,但是子外壳永远不能修改父环境。 当您想要更改执行这些命令的外壳环境(例如通过设置变量),但又不想更改脚本本身正在运行的环境时,这是理想的。当您想要运行子外壳时,也希望使多个长时间运行的进程同时在后台启动并运行。 一个外壳程序可以产生多个子外壳程序,而这些子外壳程序又可以递归地产生任何数量的自己的子外壳程序。 图1说明了该过程。

图1.子shell如何与其父shell进行交互
该图显示了子外壳如何与其父外壳交互。

Shell有时会自动生成自己的子Shell,例如在管道上使用内置插件时。 在子外壳程序中时,shell $参数将扩展为父外壳程序的进程ID(PID),而不是子外壳程序的。

在子Shell中运行命令

要在子shell中运行一组命令,请将其括在括号中。 您可以使用重定向将输入发送到子Shell的标准输入,或将其集体输出发送到文件或管道。

尝试在主目录中键入清单17中显示的内容。 如果您还没有名为example的目录,它将创建一个示例目录和一些测试文件。

清单17.在子shell中制作一组文件
$ pwd
/home/user
$ (mkdir example; cd example; touch A B C)
$ pwd
/home/user
$ cd example; ls
A B C
$ pwd
/home/user/example
$

在此示例中,外壳程序生成一个在后台运行的子外壳程序,创建示例目录并使用touch在该目录中创建三个虚拟文件。 同时,shell返回到主目录中的提示符。

当您有一组需要花费很长时间才能执行的命令时,子命令行在命令行和脚本中都很方便。 要使外壳自由,可以在后台运行它,或在后台运行其中的许多命令:

( group-of-long-running-commands ) &
( another-group-of-long-running-commands ) &
( yet-another-group-of-long-running-commands ) &

子壳和变量

重要的是要了解变量如何与子外壳一起使用。 由于subshel​​l环境是其父级环境的副本,因此它将继承父级的所有变量。 但是父外壳程序永远不会看到在子外壳程序环境中所做的任何更改,而子外壳程序再也不会看到在生成子外壳程序之后在父外壳程序中所做的任何更改。

例如,使用vi编辑器将清单18中的脚本保存到您的主目录中的名为vartest的文件中,并使其可执行(请参见“ 制作shell脚本”部分)。

清单18. Shell脚本演示了子shell中的变量行为
#!/bin/sh
# Demonstrates variable behavior in a subshell environment
 
VAR=10

echo "VAR is" $VAR

(

echo "In the subshell, VAR is still" $VAR

VAR=$(($VAR+5))

echo "The new value of VAR in the subshell is" $VAR

)

echo "Outside of the subshell, VAR is" $VAR

现在,尝试通过键入脚本名称来执行该脚本,如清单19所示。

清单19. vartest脚本的输出
$ vartest
VAR is 10
In the subshell, VAR is still 10
The new value of VAR in the subshell is 15
Outside of the subshell, VAR is 10
$

连续循环

现在该看一下循环了,循环使您可以执行迭代任务,例如对一组文件执行某些操作或命令。 外壳程序有几种构造循环的方法。

进行for循环

最常见的循环构造是for循环。 它首先定义一个变量来命名循环,给出一个成员列表,该成员列表可以是任何单词,包括整数和文件名,然后给出要在每次迭代中执行的命令。 每个命令都以分号(;)终止,整个分组都用dodone括起来。 清单20中描述了它的结构。

清单20. for循环的shell结构
for loopname in members

do

	command;
	command;
	...
	command;

done

在循环的第一次迭代中, loopname变量采用第一个成员的值。 然后,将loopname的值替换为列表中下一个成员的值,并继续进行此迭代,直到没有更多成员为止。

在大多数shell中, dodone可以用大括号代替,如清单21所示。

清单21. shell的for循环的替代结构
for loopname in members

{

	command;
	command;
	...
	command;

}

输入清单22中的文本以运行一个由三个成员组成的简单循环:

清单22.通过循环更改变量的值
$ for i in 1 2 3
> {
> VAR = $(($VAR+$i))
> echo $i:$VAR
> }
1:1
2:3
3:6
$

在目录中的每个文件上执行命令

您可以使用循环在一组给定的文件上执行一个命令或一组命令。 如果您将文件名指定for循环的成员,则循环将按照您赋予它们的顺序对每个文件进行操作。 您可以给同一个文件两次,然后循环依次对其进行操作。 使用清单23中的文本在示例目录中进行尝试。

清单23.用文件列表构造一个循环
$ cd ~/example
$ ls
A B C
$ for file in C B B C
> {
> echo $file
> }
C
B
B
C
$

要对目录中的每个文件执行操作,请使用星号( * )作为循环的唯一成员,如清单24所示。 Shell将其扩展到目录中的所有文件。 然后,对于希望对所有文件进行操作的循环内命令,请使用loopname变量作为适当的参数或选项。

清单24.在目录中的所有文件上执行命令
$ ls
A B C
$ for file in *
> {
> mv $file $((0x$file))
> }
$

如果您一直在运行本教程中的所有示例,则应更改示例目录的内容:

$ ls
10 11 12
$

发生的是循环中的mv命令将文件名从十六进制值(通过在名称开头插入0x构成)更改为十进制等效值。

循环播放

您可以使循环运行的时间只要满足某些条件即可。 使用while条件执行此操作, 清单25中描述了它的格式。

清单25. shell while循环的结构
while [ condition ]; do

	command;
	command;
	...
	command;
done

在循环中, 条件可以是使用运算符构建的语句(请参见表3 ),也可以像变量名一样简单。 只要该值不为零,就可以。

表3.常见的shell运算符
操作员 描述
-eq 等于
-ne 不等于
-lt 少于
-le 小于或等于
-gt 比...更棒
-ge 大于或等于

在构造while循环时,需要牢记一些注意事项。 首先, 条件与其周围的方括号之间必须始终有空白 。 其次,如果将变量用于条件中的数字比较, 则必须先在while语句之前定义该变量 。

输入清单26中的文本以执行简短的while循环:

清单26.使用while循环更改变量
$ VAR=0
$ while [ $VAR -lt 10 ]; do
>   echo $VAR;
>   VAR=$(($VAR+1));
> done
0
1
2
3
4
5
6
7
8
9
$

进行直到循环

until条件与while类似,并且使用相同的运算符,但相反。 仅在条件为假时才执行循环,并且循环将继续进行迭代, 直到给定条件变为真为止 。 清单27中描述了它的格式。

清单27. Shell直到循环的结构
until [ condition ] ; do

	command;
	command;
	...
	command;
done

通过输入清单28中的文本,尝试运行一个short until循环:

清单28.用直到循环更改变量
$ VAR=10
$ until [ $VAR -eq 0 ]; do
>   echo $VAR;
>   VAR=$(($VAR-1));
> done
10
9
8
7
6
5
4
3
2
1
$

嵌套多个循环

您可以嵌套循环并组合各种循环以执行各种复杂的操作。 由于for循环的元素不必是数字,也不是按时间顺序排列,因此您可以使用命令名称稍后在某些内部循环中作为命令执行,例如printfechostopresume等。

尝试运行清单29中的示例。 这是一个until嵌套在for循环中(循环词不是数字顺序)时执行算术替换的until循环。

清单29.用嵌套循环进行算术替换
$ for i in 250 100 2136 875
>  {
>    VAR=10;
>    until [ $VAR -eq 0 ]; do
>      echo "$i / $VAR = $(($i/$VAR))  $i * $VAR = $(($i*$VAR))\
             $i + $VAR = $(($i+$VAR))  $i - $VAR = $(($i-$VAR))";
>      VAR=$(($VAR-1);
>    done;
>  }
250 / 10 = 25  250 * 10 = 2500  250 + 10 = 260  250 - 10 = 240
250 / 9 = 27  250 * 9 = 2250  250 + 9 = 259  250 - 9 = 241
250 / 8 = 31  250 * 8 = 2000  250 + 8 = 258  250 - 8 = 242
250 / 7 = 35  250 * 7 = 1750  250 + 7 = 257  250 - 7 = 243
250 / 6 = 41  250 * 6 = 1500  250 + 6 = 256  250 - 6 = 244
250 / 5 = 50  250 * 5 = 1250  250 + 5 = 255  250 - 5 = 245
250 / 4 = 62  250 * 4 = 1000  250 + 4 = 254  250 - 4 = 246
250 / 3 = 83  250 * 3 = 750  250 + 3 = 253  250 - 3 = 247
250 / 2 = 125  250 * 2 = 500  250 + 2 = 252  250 - 2 = 248
250 / 1 = 250  250 * 1 = 250  250 + 1 = 251  250 - 1 = 249
100 / 10 = 10  100 * 10 = 1000  100 + 10 = 110  100 - 10 = 90
100 / 9 = 11  100 * 9 = 900  100 + 9 = 109  100 - 9 = 91
100 / 8 = 12  100 * 8 = 800  100 + 8 = 108  100 - 8 = 92
100 / 7 = 14  100 * 7 = 700  100 + 7 = 107  100 - 7 = 93
100 / 6 = 16  100 * 6 = 600  100 + 6 = 106  100 - 6 = 94
100 / 5 = 20  100 * 5 = 500  100 + 5 = 105  100 - 5 = 95
100 / 4 = 25  100 * 4 = 400  100 + 4 = 104  100 - 4 = 96
100 / 3 = 33  100 * 3 = 300  100 + 3 = 103  100 - 3 = 97
100 / 2 = 50  100 * 2 = 200  100 + 2 = 102  100 - 2 = 98
100 / 1 = 100  100 * 1 = 100  100 + 1 = 101  100 - 1 = 99
2136 / 10 = 213  2136 * 10 = 21360  2136 + 10 = 2146  2136 - 10 = 2126
2136 / 9 = 237  2136 * 9 = 19224  2136 + 9 = 2145  2136 - 9 = 2127
2136 / 8 = 267  2136 * 8 = 17088  2136 + 8 = 2144  2136 - 8 = 2128
2136 / 7 = 305  2136 * 7 = 14952  2136 + 7 = 2143  2136 - 7 = 2129
2136 / 6 = 356  2136 * 6 = 12816  2136 + 6 = 2142  2136 - 6 = 2130
2136 / 5 = 427  2136 * 5 = 10680  2136 + 5 = 2141  2136 - 5 = 2131
2136 / 4 = 534  2136 * 4 = 8544  2136 + 4 = 2140  2136 - 4 = 2132
2136 / 3 = 712  2136 * 3 = 6408  2136 + 3 = 2139  2136 - 3 = 2133
2136 / 2 = 1068  2136 * 2 = 4272  2136 + 2 = 2138  2136 - 2 = 2134
2136 / 1 = 2136  2136 * 1 = 2136  2136 + 1 = 2137  2136 - 1 = 2135
875 / 10 = 87  875 * 10 = 8750  875 + 10 = 885  875 - 10 = 865
875 / 9 = 97  875 * 9 = 7875  875 + 9 = 884  875 - 9 = 866
875 / 8 = 109  875 * 8 = 7000  875 + 8 = 883  875 - 8 = 867
875 / 7 = 125  875 * 7 = 6125  875 + 7 = 882  875 - 7 = 868
875 / 6 = 145  875 * 6 = 5250  875 + 6 = 881  875 - 6 = 869
875 / 5 = 175  875 * 5 = 4375  875 + 5 = 880  875 - 5 = 870
875 / 4 = 218  875 * 4 = 3500  875 + 4 = 879  875 - 4 = 871
875 / 3 = 291  875 * 3 = 2625  875 + 3 = 878  875 - 3 = 872
875 / 2 = 437  875 * 2 = 1750  875 + 2 = 877  875 - 2 = 873
875 / 1 = 875  875 * 1 = 875  875 + 1 = 876  875 - 1 = 874
$

读取键盘输入

您也可以在脚本中或从命令行本身读取键盘输入。 这是通过read命令完成的, read命令是一个内置函数,它将任意数量的变量名作为参数。 它从标准输入中读取值到变量中,读取单行输入并将每个输入字分配给每个变量。

尝试读取一个变量,如清单30所示:

清单30.使用read读取变量
$ read VAR23
$ echo $VAR
23
$

使用-p选项为每次读取提示。 将提示输入为带引号的字符串,如清单31所示。 发生变量扩展。

清单31.使用带有读取变量的提示
$ read -p "Instead of $VAR, what number would you like? " VAR
Instead of 23, what number would you like? 17
$ echo $VAR
17
$

如果键盘输入中的单词多于变量,则依次为变量分配输入单词,直到到达最后一个变量,然后最后一个变量被分配给输入行的其余部分。 (如果输入中给出的单词少于变量,那么将为变量分配值,直到分配了所有输入,然后为所有其余变量赋空值。)

循环阅读

您可以将read用作循环中的条件。 现在使用清单32尝试一下:

清单32.循环读取文件名列表
$ while read -p "File? " file; do ls $file; done
File? 10
10
File? 12
12
File? 42
42: no such file or directory
File? 
10 11 12
File? 
$

当对循环的输入进行管道传输时,通常使用此技术。 尝试键入清单33中的文本,该文本将ls命令的输出替换为循环:

清单33.从管道读取
$ ls | while read file; do ls $file; done
10
11
12
$

您还可以在多行上对该变量进行操作,例如向标准输出发送消息,还可以对loopname变量执行Shell算术(请参见Shell算术和基数转换部分)。 尝试清单34中提供的示例:

清单34.带有管道读取的更长循环
$ ls | while read file; do echo "The file is " `ls -i $file`; \
echo "If the number were in hex, the value would be $((16#$file))"; done
The file is  100267120 10
If the number were in hex, the value would be 16
The file is  100267121 11
If the number were in hex, the value would be 17
The file is  100267122 12
If the number were in hex, the value would be 18
$

您可以通过一次管道read来读取多个值,如清单35所示。

清单35.从管道读取多个变量
$ ls -i | while read inode file; do \echo "File $file has inode $inode"; done
File 10 has inode 100267120
File 11 has inode 100267121
File 12 has inode 100267122
$

放在一起

本章的最后部分结合了您刚刚为一些有用的,现实生活中的单线而学到的各种技巧和技术。 它还包括一个简单的shell脚本,该脚本执行任意基本转换。

有用的单线

以下示例是执行有用功能的示例shell一线式。 它们全部由本教程中描述的各种构造组成。

  • 在当前目录中获取一组文件,它们的长度恰好是两个字符,并以.ppm扩展名重命名:

    for i in ??; { mv $i $i.ppm; }
  • 使用tar和子外壳程序复制整个目录树,同时保留所有相同的文件权限:

    ( cd source ; tar pcf - * ) | ( cd target ; tar pxvf - )
  • 读取二进制数并以十进制输出值:

    read BINLOC;echo $((2#$BINLOC))
  • 在/ usr / local目录树中找到所有扩展名为.mp3的文件-这些文件的名称中可能带有空格-并使用bzip2实用程序对其进行压缩:

    find /usr/local -name "*.mp3" | while read name ; do bzip2 $name; done
  • 将给定文件中的所有十进制数字输出为它们的十六进制值:

    cat file | while read number ; do echo $((0x$number)); done
  • 将给定文件中的所有十进制数字转换为十六进制值,并将其输出到扩展名为.hex的新文件中:

    cat file | while read number ; do echo $((0x$number)) >> file.hex; done
  • 循环执行十次迭代,运行运行命令,并以数字(从0到90,以十为增量)作为参数传递:

    i=0; while [ $i -ne 100 ]; do command $i; i=$(($i+10)); done

一个示例脚本:将数字转换为另一个基数

清单36将本教程中讨论的各种技巧组合在一起。 这是一个简单的脚本baseconv ,它将数字从给定的输入库转换为输出库。 给它输入和输出基数的值作为参数,它从键盘输入中读取数字,直到得到数字0。

清单36.一个简单的转换基数的脚本
#!/bin/sh
# baseconv, convert numbers from one base to another.
#

NUMBER=1
while [ $NUMBER ]; do
    read -p "Input base: " IN
    read -p "Output base: " OUT
    read -p "Number: " NUMBER
    bc -ql <<- EOF
		obase=$OUT
		ibase=$IN
		$NUMBER
		EOF
done

将其保存在可执行文件中后(请参见“ 制作shell脚本”部分),尝试运行它,如清单37所示:

清单37. baseconv脚本的输出
$ ./baseconv
Input base: 10
Output base: 16
Number: 33
21
Input base: 2
Output base: 1100
Number: 101
5
Input base: 16
Output base: A
Number: ACA
2762
Input base: 10
Output base: 10
Number: 
$

结论

Wrapping up

ew! 本教程肯定涵盖了很多基础知识,带您纵览了许多基本的Shell编程概念。 在学习本教程的同时,您学习了Shell编程的许多核心概念:连续循环,内联输入,读取键盘输入,基本转换和子Shell执行。 您还了解了如何直接从Shell提示符下以单一代码的形式运行Shell代码片段,以及如何将它们作为可执行脚本放入文件中。 这些是您可以学习的一些最重要的Shell脚本概念。 如果您使用本教程中学到的知识以及本系列先前教程中学到的知识,那么您就很容易成为UNIX专业人士。


翻译自: https://www.ibm.com/developerworks/aix/tutorials/au-unixtips4/index.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值