高级shell编程笔记(第十四章 命令替换)

第十四章 命令替换

命令替换将会重新分配一个命令甚至是多个命令的输出;它会将命令的输出如实的添加到另一个上下文中。
使用命令替换的典型形式是使用后置引用(`…`)。后置引用形式的命令将会产生命令行文本。

[root@localhost shell]# script_name=`basename $0`
[root@localhost shell]# echo "The name of this script is $script_name."

这样的话,命令的输出可以被当成传递到另一个命令的参数,或者保存到变量中,甚至可以用来产生for循环的参数列表

[root@localhost shell]# rm `cat filename`
#使用这种形式,可能会产生"参数列太长"的错误
#更好的方法是:xargs rm -- < filename   #(-- 同时覆盖了那些以"-"开头的文件所产生的特殊情况)

[root@localhost shell]# textfile_listing=`ls *.txt`
[root@localhost shell]# echo $text_file_listing

[root@localhost shell]# textfile_listing2=$(ls *.txt)   #这是命令替换的另一种形式
[root@localhost shell]# echo $textfile_listing2

#将文件列表放入到一个字符串中的一个可能的问题就是可能会和混进一个新行
#一个安全的将文件列表传递到参数中的方法就是使用数组
#shopt -s nullglob   #如果不匹配。那就不进行文件名扩展。
#textfile_listing=(*.txt)

注意:命令替换将会调用一个subshell

注意:命令替换可能会引起word splitting

[root@localhost shell]# COMMAND `echo a b`   #2个参数:a 和 b

[root@localhost shell]# COMMADN "`echo a b`"   #1个参数:"a b"

[root@localhost shell]# COMMAND `echo`   #无参数

[root@localhost shell]# COMMAND "`echo`"   #一个空参数

即使没有引起word splitting,命令替换也会去掉多余的新行。

#cd "`pwd`"   #这句总会正常的工作
#然而...
[root@localhost shell]# mkdir '带尾随换行符的dir
'

[root@localhost shell]# cd '带尾随换行符的dir
'

[root@localhost shell]# cd "`pwd`"   #报错
#bash: cd: /tmp/file with trailing newline: No such file or directory

[root@localhost shell]# cd "$PWD"   #正常
old_tty_setting=$(stty -g)   #保存老的终端设置
echo "Hit a key"
stty -icanon -echo   #对终端禁用"canonical"模式和本地的echo
key=$(dd bs=1 count=1 2>/dev/null)   #使用dd命令来取得一个按键
stty "old_tty_setting"   #保存老的设置
echo "You hit ${#key} key."   #${#key}=$key中的字符数

#按键除了回车以外的键,输出"You hit 1 key"
#按回车键,输出就是"You hit 0 key"。新行已经被命令替换吃掉了

注意:当一个变量是使用命令替换的结果作为值的时候,然后使用echo命令来输出这个变量(并且不引用这个变量,就是不用引号括起来),那么命令替换将会从最终的输出中删掉换行符。这可能会引起一些异常情况。

[root@localhost shell]# dir_listing=`ls -l`
[root@localhost shell]# echo $dir_listing
total 8 -rw------- 1 root root 12 Aug 24 06:59 filename.txt -rw------- 1 root root 222 Aug 23 11:43 test.sh

[root@localhost shell]# echo "$dir_listing"   #用引号括起来
total 8
-rw------- 1 root root  12 Aug 24 06:59 filename.txt
-rw------- 1 root root 222 Aug 23 11:43 test.sh

命令替换甚至允许将整个文件的内容放到变量中,可以使用重定向或者cat命令。

viriable1=`<file1`
variable2=`cat file2`   #这行会fork一个新进程,所以这行代码将会比第一行代码执行的慢

#注意:变量中是可以包含空白的,甚至是控制字符。
#摘录自系统文件/etc/rc.d/rc.sysinit

if [ -f /fsckoptions ];then
  fsckoptions=`cat /fsckoptions`
...
fi
#
  if [ -e "/proc/ide/${disk[$device]}/media" ];then
    hdmedia=`cat /proc/ide/${disk[$device]}/media`
...
fi
#
if [ ! -n "`uname -r | grep -- "-"`" ];then
  ktag="`cat /proc/version`"
...
fi
#
if [ $usb = "1" ];then
  sleep 5
  mouseoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E "^I.*Cls=03.*Prot=02"`
  kbdoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E "^I.*Cls=03.*Prot=01"`
...
fi

注意: 不要将一个非常长的文本文件的内容设置到一个变量中,除非你有一个非常好的原因非要这么做不可。不要将 2 进制文件的内容保存到变量中。

Example 14-1 愚蠢的脚本策略

#!/bin/bash
#
dangerous_variable=`cat /boot/vmlinuz` # 这是压缩过的 Linux 内核本身
echo "string-length of \$dangerous_variable = ${#dangerous_variable}"
#这个字符串变量的长度是 $dangerous_variable = 794151
#不要使用'wc -c /boot/vmlinuz'来计算长度

#echo "$dangerous_variable"   #千万别尝试这么做! 这样将挂起这个脚本.
exit 0

注意:在这里是不会发生缓冲区溢出错误。因为这是一个解释型语言的实例,Bash 就是一种解释型语言,解释型语言会比编译型语言提供更多的对程序错误的保护措施。

变量替换允许将一个循环的输出放入到一个变量中。这么做的关键就是将循环中 echo 命令的输出全部截取。

Example 14-2 从循环的输出中产生一个变量

#!/bin/bash
#
variable1=`for i in 1 2 3 4 5
do
  echo -n "$i"
done`   #对于这里的命令替换来说,这个'echo'命令是非常关键的.

echo "variable1 = $variable1"   #variable1=12345

i=0
variable2=`while [ "$i" -lt 10 ]
do
  echo -n "$i"
  let "i += 1"
done`   #再来一个,'echo'是必须的.

echo "variable2 = $varoable2"   #variable2=0123456789
#这就证明了在一个变量声明中嵌入一个循环是可行的。
exit 0

注意: 命令替换使得扩展有效的 Bash 工具集变为可能。这样,写一段小程序或者一段脚本就可以达到目的,因为程序或脚本的输出会传到 stdout 上(就像一个标准的工具所做的那样),然后重新将这些输出保存到变量中。(译者: 作者的意思就是在这种情况下写脚本和写程序作用是一样的)

#include <stdio.h>
/* "Hello,world." C program */
int main()
{
    printf("Hello,world.")
    return (0);
}
#
bash$ gcc -o hello hello.c
#/bin/bash
#hello.sh
greeting=`./hello`
echo $greeting
#
bash$ sh hello.sh

注意:对于命令替换来说,$(COMMAND)形式已经取代了反引号。

output=$(sed -n /"$1"/p $file)
#将一个文本的内容保存到变量中。
File_contents1=$(cat $file1)
File_contents2=$(<$file2)

$(…)形式的命令替换在处理反斜线(\)时与`…`形式不同。

[root@localhost shell]# echo `echo \\`

[root@localhost shell]# echo $(echo \\)
\

$(…)形式的命令替换是允许嵌套的

word_count=$(wc -w $(ls -l | awk '{print $9}'))

或者,可以更加灵活。。。

Example 14-3 找anagram(回文构词法, 可以将一个有意义的单词, 变换为1个或多个有意义的单 词, 但是还是原来的子母集合)

#!/bin/bash
#
E_NOARGS=66
E_BADARGS=67
MINLEN=7

if [ -z "$1" ];then
  echo "Usage $0 LETTERSET"
  exit $E_NOARGS # 脚本需要一个命令行参数.
elif [ ${#1} -lt $MINLEN ];then
  echo "参数必须至少有$MINLEN字母。"
  exit $E_BADARG
fi

FILTER='.......'
Anagrams=($(echo $(anagram $1 | grep $FILTER)))
echo
echo "${#Anagrams[*]} 7 + letter anagrams found"
echo
echo ${Anagrams[0]}
echo ${Anagrams[1]}
exit $?

命令替换在脚本中使用的例子:Example 10-7,Example 10-26,Example 9-28,Example 12-3,Example 12-19,Example 12-15,Example 12-49,Example 10-13,Example 10-10,Example 12-29,Example 16-8,Example A-17,Example 27-2,Example 12-42,Example 12-43,Example 12-44。

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页