转:http://www.ibm.com/developerworks/cn/education/aix/au-unixtips4/section2.html
Shell 命令执行
学习 Shell 脚本的最佳方法是通过示例。对于您要在脚本中执行的任何命令都可以在命令行上立即尝试,这也是本教程通篇提供大量实践示例的原因所在。例如,echo
命令将一行文本写入到标准输出。(许多 Shell 以内置命令形式提供其自己版本的 echo
命令,包括 IBM AIX® 的 Bourne Shell 实现。如果这也是您的现实情况,那么当您运行 echo
时,实际上正在运行您的 Shell 版本的命令。)
尝试在使用 echo
输出短消息时加引号:
$ echo "Hello, world" Hello, world |
Shell 引用(无论在命令行还是在脚本中加注)是一种将字符串传递给 Shell 的方法,可以避免对字符串中可能包含的任何特殊元字符产生混淆。当字符串包含一个以上的单词或者段落包含空格字符时使用引用。如果单个字符恰好是 Shell 元字符,并且您想去除它的特殊含义,就可以在两边加上引号,例如,当您要传递一个美元符号 ($
) 作为字面上的美元符号字符而不是作为变量名前的特殊元字符时。
在引用的文本内部发生各种扩展。例如,在双引号括起来的文本中,变量被展开为它们的值,而单引号括起来的文本内部引用的变量名则不展开。
有三种重要的引用类型需要了解:
-
通过在前面加反斜杠 (\) 引用单个字符。这样只会传替字符的字面含义,而非它可能包含的任何特殊含义,比如空格符或 Shell 元字符。例如,使用
\*
引用一个星号 (*),它是 Shell 元字符。要引用真正的反斜杠字符,可以使用\\
。 -
通过在文本字符串两边加双引号 (
"
) 来传递扩展的引用。美元符号 ($
) 和单引号 ('
) 字符会保留其自身含义。因此,和其他字符一起在引用中出现的任何变量名都会被它们的值所替代。新行或特定字符 ($`"\
) 前的反斜杠被移除,但是引用的字符会被传递。 -
使用单引号 (
'
) 将文本括起来以传递文本字符串的字面引用,所有的变量名、元字符等都作为文字字符,而不它们的含义或值来传递。
请注意在不同的 Shell 中引用的确切规则会有所区别。参考您所使用的特殊 Shell 的 man
页面来了解准确规则。
分配一个变量,然后尝试使用各种引用格式输出该变量,如清单 1 中所示。
$ 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" |
注意解释变量的方式取决于所使用的引用格式。
在 Shell 中,以井号 (#
) 开始一个注释行。井号及其后面跟随的同一行的所有内容都被忽略。尝试输入几行夹杂注释的文本,如清单 2中所示:
$ # 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 |
这是 Shell 自己使用的一种特殊注释,用于确定文件的语言或目录。感叹号在 UNIX 和排版术语中常常被称为 bang,后面跟随的路径名告诉 Shell 应该使用来执行该文件的解释器。在本例中是 /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 脚本的简短示例。
#!/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
包含一组以冒号分隔的目录。它就是您的路径,Shell 总是会“看到”这些目录中的所有文件。UNIX Path 的目的是为了便于运行二进制文件。这就是为什么您只需要键入命令的基本文件名,比如 ls
和 echo
,而不用提供它们的完整或相对路径名。如果您将脚本移动到 Path 中的目录,那么只需键入它的名字就可以运行。具体的 Path 取决于您的 UNIX 实现和本地设置,但 Path 中的目录通常包括 /bin、/usr/bin 和 /usr/local/bin。
一些用户对它们的 Shell 进行配置,从而使 PATH
变量包括当前的工作目录,这在 Path 中以点字符 (".
") 表示。如此一来,要在当前目录下运行脚本,只需要键入它的名称,不需要指出相对目录。,Shell 按给定的顺序搜索 Path中的目录,从而避免中木马或发生异常情况,一种极其不明智的做法是把当前工作目录放在 Path 的末尾。
要查看您的 Path,可以使用 echo
显示 PATH
变量的内容,如清单 4 所示。
$ 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 的完整选项列表。
尝试在您当前的交互式 Shell 中设置了哪些选项。通过使用 echo
显示 -
变量的内容来完成这项任务:
$ echo $- himBH $ |
参考您使用的 Shell 的 man
页面来获取当前的标志和选项列表。表 1 提供了 AIX® 上的 Bourne Shell 的常用标志列表,以及对每种标志作用的简要说明。
-a |
导出所有已分配值的变量。 |
-cVariable |
执行从变量 中读取的命令。 |
-e |
当命令满足以下条件之一时立即退出:命令退出时返回比 0 大的值;命令不是 while 、until 或 if 结构的一部分;命令不经过 AND 或 OR 检测;或者命令不是管道前加感叹号。 |
-f |
禁用所有文件名替换。 |
-h |
定义函数时,定位和记住函数内部调用的所有命令。 |
-i |
指定交互式 Shell。 |
-k |
将所有关键字 都放入命令的环境。 |
-n |
读取命令,但是不执行它们。 |
-r |
调用受限制的 Shell。 |
-s |
从标准输入读取命令,然后将输出写入标准错误(不包括 Shell 内置命令的输出)。 |
-t |
读取并执行单个命令,然后退出。 |
-u |
在脚本中,将所有未定义 变量视为错误。当尝试变量替换时退出。 |
-v |
当读取输入行时将其显示出来。 |
-x |
在执行命令之前显示其完整命令(包括所有的参数和选项)。 |
Shell 运算和进制转换
Shell 提供大量的基本运算操作,在脚本中非常有用。Shell 对您提供的算术表达式求值,执行运算展开式,此时使用得出的结果替换表达式。以下面的格式提供运算表达式:
$(( expression )) |
您可以使用 echo
在命令行显示运算展开式的结果,了解其工作情况。现在尝试清单 5 所显示的结果。
$ echo $((10+40)) 50 $ echo $((5*(3+3))) 30 |
您还可以将展开式分配给变量。尝试清单 6 所显示的结果。
$ 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 语言的规则来确定。
+ |
加 |
- |
减 |
* |
乘 |
/ |
除 |
% |
求余 |
< |
小于(1 代表真,0 代表假) |
<= |
小于等于(1 代表真,0 代表假) |
> |
大于(1 代表真,0 代表假) |
>= |
大于等于(1 代表真,0 代表假) |
<< |
按位向左移位:将给定的整数或第一个表达式向左移动第二个表达式表示的位数 |
>> |
按位向右移位:将给定的整数或第一个表达式向右移动第二个表达式表示的位数 |
假定在您的脚本中有一些数字,您需要以另外的进制处理这些数字。使用 Shell 运算可以很容易地自动实现这类转换。一种情况是使用 Shell 运算把一个数字从给定的进制转换位十进制。如果数字以运算展开式的形式提供,那么假定它带有十进制符号,除非 它前面带有 0(这种情况假定是八进制)或 0x(这种情况假定是十六进制)。键入以下内容以得到一些八进制和十六进制值的十进制输出:
$ echo $((013)) $ echo $((0xA4)) |
您还可以使用以下格式指定 2 到 64 之间的任意进制:
$((BASE#NUMBER)) |
通过在 Shell 提示符后键入清单 7 中所示的行,尝试将二进制、八进制、十六进制以及其他进制的数转换为十进制。
echo $((2#1101010)) echo $((8#377)) echo $((16#D8)) echo $((12#10)) echo $((36#ZZYY)) |
在 Shell 中进行进制转换的另一个诀窍是使用 bc
,它是一种任意精度运算语言,大多数 UNIX 安装程序都提供。因为它允许您指定输出进制,所以当您需要以十进制以外的进制输出时,这是一种很好的技术。
bc
的特殊变量 ibase
和 obase
分别包含用于输入和输出的进制的值。缺省情况下,都被设置为 10。要执行进制转换,需要改变其中的一个或两个值,然后提供一个数字。立即尝试,如清单 8 中所示。
$ bc -ql 10 10 obase=16 10 A ibase=2 10 2 Control-D $ |
要快速执行进制转换,可以联合使用 bc
和 echo
形成快捷的单命令行程序,将给定的值通过管道传输给 bc
。键入清单 9 中显示的内容。
$ echo 'obase=16; 47' | bc 2F $ echo 'obase=10; ibase=16; A03' | bc 2563 $ |
警告:当您设置 bc
的输入进制以后,输入 bc
的所有数字都使用该进制,包括您提供用于设置输出进制的数字。因此最好先设置输出进制,否则可能会产生意想不到的结果,如清单 10 中所示。
$ echo 'ibase=16; obase=10; A' | bc A $ echo 'ibase=16; obase=A; A' | bc 10 $
|
内联输入
尽管 echo
通过管道将内容传递给交互式命令(比如 bc
)可以生成快捷的单命令行程序,但是它对于多行输入并不适用,比如可能用到实际文件中的内容。但是另外一种有用的方法可以完成这个任务。Shell 有一种工具称为 here documents 或内联输入,这是一种动态构建文件的非常好的方法,比如用于脚本内部,并且将该文件的内容重定向到一个命令。
使用 Shell <<
操作符来指定一个 here document,然后在同一行的后面跟上一个限定字符串,该字符串标记输入的结束,并且您可以选择任何文本,只要是不包含空格字符的单个词都可以。其后跟随构成您的输入文件的行,然后以独占一行的限定字符串结束输入,在它的前面或后面不能有任何文本,否则该行将被视为输入的一部分。使用 cat
进行尝试,如清单 11 中所示。
$ cat << END > END of input text > ENDspace > 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 document 发送给 cat
来完成,如清单 12 中的脚本所示。使用 vi
输入该脚本并保存到名为 baseconv 的文件中,并且将该文件设置为可执行文件(请参见创建 Shell 脚本部分)。
清单 12. 使用 here document 提供 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 document 的内容被发送到(使用 cat
)标准输出。立即尝试,如清单 13 中所示。
清单 13. 从 here document 输出 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 的大多数实现允许出现使用可选的连字符重定向的内联输入。可选的连字符将所有的前导 Tab 字符从所有输入行的前面去掉,也包括包含限定字符串的行。这对于您希望让编写的脚本保持当前缩进时会有帮助。由于内联输入通常逐字读取,并且限定字符串必须在行的开始处给出,因此输入将打乱您的当前缩进并使脚本看起来不雅观。因此,您可以重写清单 12 中的脚本,使其与清单 14 一致,而输出不会改变。
清单 14. 带前导缩进的 Shell 脚本 here document
#!/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 document 代替实际文件,或代替任意行的实际输入。
尝试使用 here document 将多行输入发送到 bc
。键入清单 15 中显示的内容。
$ bc << EOF > ibase=16 > A > EOF 10 $ |
通常使用内联输入来扩展变量。尝试清单 16 中显示的内容。
$ BASECON=16 $ bc << EOF > ibase=16 > $BASECON > EOF 22 $
|
Subshell 执行
可以在一个名为 subshell 的新 Shell 中执行一个或一组命令,当前 Shell 是 SubShell 的父 Shell。Subshell 继承父亲的环境。I/O 重定向可以出现在子 Shell 和父 Shell 之间,但是 Subshell 永远不能修改父环境。当您为了执行这些命令(比如设置变量)要更改 Shell 的环境,并且不想更改脚本自身运行所在的环境时,这就是您所期望的技术。当您想要同时在后台启动多个长时间运行的进程时也最好使用 Subshell。一个 Shell 可以生成多个 Subshell,而 Subshell 又可以循环生成属于它们自身的任意数量的 Subshell。图 1 说明了这个过程。
Shell 有时自动生成自身的 Subshell,比如在管道中使用内置命令时。在 Subshell 中,Shell $
参数扩展到父 Shell 而不是 Subshell 的进程 ID (PID)。
要在 Subshell 中运行一组命令,可以使用括号将其括起来。您可以使用重定向将输入发送到 Subshell 的标准输入,或将 Subshell 的集合输出发送到文件或管道。
尝试在您的 home 目录键入清单 17 中显示的内容。该示例创建一个 example 目录和一些测试文件,前提是原来不存在 example 目录。
$ pwd /home/user $ (mkdir example; cd example; touch A B C) $ pwd /home/user $ cd example; ls A B C $ pwd /home/user/example $ |
在本例中,Shell 生成一个在后台运行的 Subshell,建立 example 目录,然后使用 touch
在该目录中生成三个虚拟文件。同时,Shell 返回 home 目录的命令行。
当您有一组执行时间长的命令时,在命令行和脚本中使用 Subshell 都很方便。为了让 Shell 保持空闲,您可以在后台运行 Subshell,或者在后台运行许多个 Subshell。
( group-of-long-running-commands ) & ( another-group-of-long-running-commands ) & ( yet-another-group-of-long-running-commands ) & |
理解变量与 Subshell 的交互方式非常重要。因为 Subshell 环境是其父亲的副本,所以它继承了父亲的所有变量。但是父 Shell 从不会看到 Subshell 环境发生的任何变化,同样,Subshell 生成以后,再也不会看到父亲发生的任何变化。
作为示例,使用 vi
编辑器将清单 18 中的脚本保存到 home 目录的 vartest 文件中,然后将其设置为可执行(请参见编写 shell 脚本部分)。
清单 18. 演示 Subshell 中变量行为的 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 中所示。
$ 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 $
|
连续循环
现在来看循环,它允许您执行重复任务,比如对一组文件执行一些操作或命令。Shell 有几种构造循环的方法。
最常见的循环结构是 for
循环。首先定义一个变量作为循环的名称,提供一组成员,可以是包括整数和文件名在内的任何单词,然后提供每次重复执行的命令。每个命令都以分号结束 (;),整个命令组以位于单词 do
和 done
之间。清单 20 描述了它的结构。
for loopname in members do command; command; ... command; done |
在循环的第一次重复中,loopname 变量获取第一个成员的值。然后 loopname 的值被清单中下一个成员的值替代,接下来它继续重复直到遍历所有成员。
在大多数 Shell 中,do
和 done
都可以被大括号所替代,如清单 21 中所示。
for loopname in members { command; command; ... command; } |
键入清单 22 中的文本来运行包含三个成员的简单循环:
$ for i in 1 2 3 > { > VAR = $(($VAR+$i)) > echo $i:$VAR > } 1:1 2:3 3:6 $ |
您可以使用循环针对给定的一组文件执行一个或一组命令。如果您提供文件的名称作为 for
循环的成员,那么循环按您提供名称的顺序在每个文件上执行操作。您可以两次提供同一个文件,循环将依次对该文件执行操作。在您的 example 目录中尝试使用清单 23 中的文本执行上述操作。
$ cd ~/example $ ls A B C $ for file in C B B C > { > echo $file > } C B B C $ |
要对同一目录下的所有文件执行操作,可以使用星号 (*
) 作为循环的唯一成员,如清单 24 中所示。Shell 将星号扩展为目录中的所有文件。然后,对于循环中您要对所有文件执行的命令,使用 loopname 变量作为合适的参数或选项。
$ ls A B C $ for file in * > { > mv $file $((0x$file)) > } $ |
如果您正在运行本教程中的所有示例,那么您的 example 目录中的内容应该已改变:
$ ls 10 11 12 $ |
发生的情况是循环中的 mv
命令将文件的名称从十六进制值(通过在名称的前面插入 0x
构成)更改为与它相等的十进制值。
您可以构造一种当满足某些条件就一直运行的循环。使用 while
条件语句来实现这一目标,其格式如清单 25 所示。
while [ condition ]; do command; command; ... command; done |
在循环中,condition 可以是使用操作符(请参见表 3)构建的语句,或者可以像一个变量名那样简单。只要值是非 0 的,就代表真。
-eq |
等于 |
-ne |
不等于 |
-lt |
小于 |
-le |
小于等于 |
-gt |
大于 |
-ge |
大于等于 |
构造 while
循环时,有一些注意事项需要牢记在心。首先,在条件与将它括起来的括号之间必须留有空白字符。其次,如果在条件中将变量用于数字比较,那么在 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 中说明。
until [ condition ] ; do command; command; ... command; done |
通过键入清单 28 中所示的内容尝试运行一个简短的 until
循环:
$ VAR=10 $ until [ $VAR -eq 0 ]; do > echo $VAR; > VAR=$(($VAR-1)); > done 10 9 8 7 6 5 4 3 2 1 $ |
您可以嵌套循环和组合多种类型的循环来执行各种类型的复杂操作。由于 for
循环的成员不必是数字或以任意类型的顺序排列,因此您可以使用稍后在某个内部循环中作为命令执行的命令名称作为其成员,比如 printf
、echo
、stop
、resume
,等等。
尝试运行清单 29 中的示例。这是一个执行算术替换的 until
循环,同时嵌套在循环词未按数字顺序排列的 for
循环内部。
$ 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
命令可以实现这一功能,这是一个内置函数,将任意数量的变量名作为参数。它从标准输入读取变量的值,读入单行输入并将各个输入词分配给各个变量。
尝试读取一个变量,如清单 30 中所示:
$ read VAR 23 $ echo $VAR 23 $ |
使用 -p
选项为每次 read 提供提示。使用以引号括起来的字符串提供提示,如清单 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 中的内容尝试这一操作:
$ while read -p "File? " file; do ls $file; done File? 10 10 File? 12 12 File? 42 42: no such file or directory File? Carriage return 10 11 12 File? Control-C $ |
此技术通常在对循环的输入使用管道时使用。尝试键入清单 33 中的文本,该文本使用循环替代 ls
命令的输出:
$ ls | while read file; do ls $file; done 10 11 12 $ |
您还可以跨多行操作变量,比如将一条消息发送到标准输出,然后对 loopname 变量执行 Shell 运算(请参见 Shell 运算和进制转换部分)。尝试清单 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 中所示。
$ 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
和 Subshell 复制整个目录树,同时保持相同的文件权限:( 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 以 10 递增)作为传递的参数运行 command:
i=0; while [ $i -ne 100 ]; do command $i; i=$(($i+10)); done
本教程中讨论的一些诀窍在清单 36 中被组合在一起。它是一个示例脚本——baseconv
,将数字从给定的输入进制转换为输出进制。为它提供输入进制和输出进制的值作为参数,然后它从键盘输入读取数字,直到读取了数字 0。
#!/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 中所示:
$ ./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: Carriage return $
|
相关推荐
【UNIX新手指南】这篇文档是为初学者设计的,旨在教授一些实用的Shell脚本技巧,帮助用户掌握自动化处理特殊任务的方法。Bourne Shell是其中的重点,它是一种广泛使用的命令解释器,尤其是在Linux和UNIX系统中。文档...
STM32+OLED_净水器水流量计源码.rar
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
,,基于EKF的三相PMSM无传感器矢量控制,基于卡尔曼滤波器的无速度传感器 ,核心关键词:基于EKF的三相PMSM无传感器矢量控制; 基于卡尔曼滤波器的无速度传感器。,基于EKF与卡尔曼滤波器的三相PMSM无传感器矢量控制研究
在智慧城市建设的大潮中,智慧园区作为其中的璀璨明珠,正以其独特的魅力引领着产业园区的新一轮变革。想象一下,一个集绿色、高端、智能、创新于一体的未来园区,它不仅融合了科技研发、商业居住、办公文创等多种功能,更通过深度应用信息技术,实现了从传统到智慧的华丽转身。 智慧园区通过“四化”建设——即园区运营精细化、园区体验智能化、园区服务专业化和园区设施信息化,彻底颠覆了传统园区的管理模式。在这里,基础设施的数据收集与分析让管理变得更加主动和高效,从温湿度监控到烟雾报警,从消防水箱液位监测到消防栓防盗水装置,每一处细节都彰显着智能的力量。而远程抄表、空调和变配电的智能化管控,更是在节能降耗的同时,极大地提升了园区的运维效率。更令人兴奋的是,通过智慧监控、人流统计和自动访客系统等高科技手段,园区的安全防范能力得到了质的飞跃,让每一位入驻企业和个人都能享受到“拎包入住”般的便捷与安心。 更令人瞩目的是,智慧园区还构建了集信息服务、企业服务、物业服务于一体的综合服务体系。无论是通过园区门户进行信息查询、投诉反馈,还是享受便捷的电商服务、法律咨询和融资支持,亦或是利用云ERP和云OA系统提升企业的管理水平和运营效率,智慧园区都以其全面、专业、高效的服务,为企业的发展插上了腾飞的翅膀。而这一切的背后,是大数据、云计算、人工智能等前沿技术的深度融合与应用,它们如同智慧的大脑,让园区的管理和服务变得更加聪明、更加贴心。走进智慧园区,就像踏入了一个充满无限可能的未来世界,这里不仅有科技的魅力,更有生活的温度,让人不禁对未来充满了无限的憧憬与期待。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
书房中如何利用镜面增加空间感与光线
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
,,pmsm电阻电感磁链常数辨识源码 电阻,电感,磁链常数辨识。 程序在ti dsp实现。 在ti开源foc框架基础上开发。 能够辨识电机电阻,电感,磁链常数。 精度较高,能够满足foc控制需要。 辨识时间短,大约两秒完成电阻电感辨识。 磁链辨识需要电机旋转。 多次辨识,结果一致性好。 辨识部分代码不包含寄存器操作,易于跨平台移植。 辨识大致原理: 电阻辨识发一个固定的电压矢量,检测电流 电感辨识发一个高频旋转的电压矢量,检测电流,计算感抗。 磁链辨识通过if控制让电机旋转,通过电压电流模型计算转子磁链分量。 ,PMSM; 电阻电感磁链常数辨识; TI DSP实现; TI开源FOC框架; 电机参数辨识; 高精度; 短辨识时间; 跨平台移植; 电阻辨识原理; 电感辨识原理; 磁链辨识原理。,基于TI DSP的PMSM电阻电感磁链常数快速高精度辨识源码
,,三菱,FX3U,plc程序模板和触摸屏程序模板,适用于运动轴控制,程序可以在自动的时候暂停进行手动控制,适用于一些中大型设备,可以防止某个气缸超时时,处于自动模式,能够轻松处理,处理完成后,恢复原来的气缸,解除暂停即可,思路清晰,编程效率大大提高,程序里附带和仪表的无协议通讯,并且附带最常用的手册。 ,关键词:三菱;FX3U;PLC程序模板;触摸屏程序模板;运动轴控制;自动/手动控制;气缸超时处理;无协议通讯;编程效率;最常用手册。,三菱FX3U PLC程序模板:中大型设备运动轴控制与气缸超时保护
内容概要:本文介绍了使用 Matlab 实现基于 BO(贝叶斯优化)的 Transformer 结合 GRU 门控循环单元时间序列预测的具体项目案例。文章首先介绍了时间序列预测的重要性及其现有方法存在的限制,随后深入阐述了该项目的目标、挑战与特色。重点描述了项目中采用的技术手段——结合 Transformer 和 GRU 模型的优点,通过贝叶斯优化进行超参数调整。文中给出了模型的具体实现步骤、代码示例以及完整的项目流程。同时强调了数据预处理、特征提取、窗口化分割、超参数搜索等关键技术点,并讨论了系统的设计部署细节、可视化界面制作等内容。 适合人群:具有一定机器学习基础,尤其是熟悉时间序列预测与深度学习的科研工作者或从业者。 使用场景及目标:适用于金融、医疗、能源等多个行业的高精度时间序列预测。该模型可通过捕捉长时间跨度下的复杂模式,提供更为精准的趋势预判,辅助相关机构作出合理的前瞻规划。 其他说明:此项目还涵盖了从数据采集到模型发布的全流程讲解,以及GUI图形用户界面的设计实现,有助于用户友好性提升和技术应用落地。此外,文档包含了详尽的操作指南和丰富的附录资料,包括完整的程序清单、性能评价指标等,便于读者动手实践。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
自驾游中的导航技巧提升
各个操作系统版本的gdal2.4库(包括win32、win64、centos7、centosAarch64、c#、linux32、ubuntu64)。 GDAL(Geospatial Data Abstraction Library)是一个在X/MIT许可协议下的开源栅格空间数据转换库。以下是对GDAL库的详细介绍: 全称:Geospatial Data Abstraction Library 性质:开源栅格空间数据转换库 用途:进行数据转换和处理 开发语言:C/C++ 数据格式支持:GDAL支持大量的栅格和矢量数据格式,包括常见的地理空间数据格式如GeoTIFF、ESRI Shapefile、GeoJSON、NetCDF、GML等,以及一些专用格式。 数据读取和写入:GDAL可以从不同的数据源中读取地理空间数据,例如文件、数据库、网络服务等,并且可以将数据写入到不同的输出格式。 数据转换和处理:GDAL可以进行各种数据转换和处理操作,包括坐标系转换、重采样、镶嵌、裁剪、投影变换等。此外,它还提供了图像处理和分析功能,如颜色空间转换、直方图均衡化、图像融合、图像代数等。
漫画作品与人工智能想象
,,FPGA以SPI模式读写SD卡,已经下板验证通过。 可移植到任何FPGA之中。 ,核心关键词:FPGA; SPI模式; SD卡读写; 下板验证; 可移植性。,FPGA SPI模式SD卡读写技术,移植通用性极强
,,永磁直驱风力发电机并网仿真,机侧采用最大功率跟踪控制,应用尖速比控制和爬山搜索法组合,电机采用单位功率因数控制,进行弱磁控制,网侧采用逆变器并网,跟踪效果理想。 多种风力变,同时附赠双馈式风力发电机。 ,永磁直驱风力发电机;并网仿真;最大功率跟踪控制;尖速比控制;爬山搜索法;单位功率因数控制;弱磁控制;逆变器并网;风力变换;双馈式风力发电机。,永磁直驱风力发电:双控策略并网仿真及弱磁双馈式应用
先休息休息沙发上饭撒的方式