shell 变量保护 引号的作用


前言

引用是 shell 脚本内部的一部分,因此专门设置本节来阐明它在所有 shell 中的用法。如果您经常遇到引用方面的语法错误,学习本节之后将确保您能够熟悉如何使用它们,特别是当脚本中包含有类似 grep、sed 和 awk 的命令时。


一、知识铺垫

反斜线

  1. 位于一个字符之前,转义该字符。
  2. 与将一个字符放在单引号之内相同。

单引号

  1. 必须匹配。
  2. 保护除以下之外的所有元字符免被解释。
    a. 自身
    b. 感叹号(仅 csh 适用)
    c. 反斜线

双引号

  1. 必须匹配。
  2. 保护除以下之外的所有元字符免被解释。
    a. 自身
    b. 感叹号(仅 csh 适用)
    c. 用于变量替换的$(例如:$(command))
    d. 用于命令替换的反引号` `(例如:` command `)

反引号

shell 程序使用反引号进行命令替换。它们与单引号和双引号无关,但通常是问题之源。例如,当复制一个 shell 脚本时,如果用单引号替换了反引号,则程序将不会工作。(误将反引号当做单引号)

 #! /bin/bash
 # Scriptname: demo0
1 now=`date`
2 echo Today is $now
3 echo "You have `ls | wc -l` files in this directory"
4 echo 'You have `ls | wc -l` files in this directory'
  [root@localhost tmp]
  [root@localhost tmp]# ./demo0 
2 Today is Thu Nov 25 11:15:21 CST 2021
3 You have 20 files in this directory
4 You have `ls | wc -l` files in this directory
  [root@localhost tmp]# 

说明:

  1. 将 UNIX/Linux date 命令的输出赋值为变量 now。反引号导致命令替换。反引号一般位于键盘上的代字符号建(~)上。
  2. 显示了变量 now 的值,也就是当前日期。
  3. 对 UNIX/Linux 管道使用反引号。ls 的输出通过管道成为 wc -l 的输入。结果是统计当前目录下文件的个数。双引号中的字符串将不会进行命令替换。输出嵌入到字符串中并被打印。
  4. 以单引号引用字符串,则其中的反引号将不会被解释,只作字面理解。

二、混合使用引号

混合使用引号是一个很有难度的问题。这一节将教您如何正确使用引号的步骤。我们将演示如何将一个 shell 变量嵌入到 awk 命令中并在不涉及 awk 字段指示符 $1 和 $2 的情况下使 shell 对变量进行扩展。

在此节,将借助 shell 调试手段来方便观察现象,使用 set -x 命令打开“执行命令的调试”,使用 set +x 命令将之关闭。

[root@localhost tmp]# echo $PATH 
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost tmp]# 
[root@localhost tmp]# set -x
[root@localhost tmp]# echo $PATH 
+ echo /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost tmp]# 
[root@localhost tmp]# 
[root@localhost tmp]# set +x     
+ set +x
[root@localhost tmp]# echo $PATH 
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

使用 set -x 命令打开调试后,每次执行命令前,都会先执行变量替换并打印命令行,打印命令行的行首会有加号(+),之后再打印命令行的执行结果。有关 shell 调试的相关内容详情请看 shell —— 调试

实例分析1

代码如下(示例):

示例

[root@localhost tmp]# name="Jacob Savage"
[root@localhost tmp]# cat datafile 
Jacob Savage:408-298-7732:934 La Barbara Dr. , San Jose, CA:02/27/78:500000
[root@localhost tmp]# 
[root@localhost tmp]# awk -F: '$1 ~ /^$name/{print $2}' datafile     
+ awk -F: '$1 ~ /^$name/{print $2}' datafile

可以看到,单引号内的字符串'$1 ~ /^$name/{print $2}' 都只作字面理解,shell 无法对单引号内的内容做变量替换和命令替换,即 datafile 文件没有匹配到 $name 模式,故输出为空。


图解分析

在这里插入图片描述
从 awk 命令左边开始,将第一个引号保持原样,在 $name 的 shell 美元符前再放置一个单引号。现在第一个单引号匹配成功,这对引号中间的字符将不受 shell 干扰。而后面的变量 $name 不在引号内,因此 shell 会对 $name 进行变量替换。接着在 $name 中的字母 e 之后再加上一个单引号,于是又匹配成功一对单引号,这对单引号结束于 awk 右花括号之后。这一对引号中的所有字母也将不会被 shell 解释了。


测试验证

[root@localhost tmp]# awk -F: '$1 ~ /^'$name'/{print $2}' datafile 
+ awk -F: '$1 ~ /^Jacob' 'Savage/{print $2}' datafile
awk: cmd. line:1: $1 ~ /^Jacob
awk: cmd. line:1:       ^ unterminated regexp
[root@localhost tmp]# 

可以看到,被单引号保护起来的左右两个字符串内容保持不变,$name 被 shell 进行了变量替换成 “Jacob Savage”。但是由于 $name 的内容带有空白符,因此 awk 命令被分割成了两部分,导致语法错误。因此,shell 变量最好使用双引号保护起来。


最终验证

[root@localhost tmp]# awk -F: '$1 ~ /^'"$name"'/{print $2}' datafile 
+ awk -F: '$1 ~ /^Jacob Savage/{print $2}' datafile
408-298-7732
[root@localhost tmp]
[root@localhost tmp]# awk -F: '$1 ~ /^"'$name'"/{print $2}' datafile 
+ awk -F: '$1 ~ /^"Jacob' 'Savage"/{print $2}' datafile
awk: cmd. line:1: $1 ~ /^"Jacob
awk: cmd. line:1:       ^ unterminated regexp
[root@localhost tmp]# 

如上所示,在第一条命令中,使用双引号保护 $name shell变量时,变量替换的结果中带有空白符,仍然保证了结果的正确。在第二条命令中,左边双引号 " 被单引号保护了起来,因此无法保护 $name,仍然导致命令错误。

在这里插入图片描述


实例分析2

代码如下(示例):

示例

[root@localhost tmp]# oldname="Ellie Main"
[root@localhost tmp]# newname="Eleanor Quigley"
[root@localhost tmp]# cat datafile 
Ellie Main:408-298-7732:934 La Barbara Dr. , San Jose, CA:02/27/78:500000
[root@localhost tmp]# 
[root@localhost tmp]# awk -F: '/^'$oldname'/{$1="'$newname'"; print $0}' datafile 
+ awk -F: '/^Ellie' 'Main/{$1="Eleanor' 'Quigley"; print $0}' datafile
awk: cmd. line:1: /^Ellie
awk: cmd. line:1:  ^ unterminated regexp
[root@localhost tmp]# 

从第一个单引号左侧开始,向行右侧移动直至到达变量 $oldname,在这个美元符号前放置另外一个单引号。将另外一个单引号放置在 $oldname 变量最后一字母 e 之后,该单引号与 $newname 美元符号前面的单引号匹配。$newname 变量最后字母 e 的单引号与行尾的单引号匹配。
这样,$oldname 与 $newname 都将被 shell 进行变量替换。但是由于变量带有空白符,导致了 awk 语法错误。


测试验证

[root@localhost tmp]# cat datafile 
Ellie Main:408-298-7732:934 La Barbara Dr. , San Jose, CA:02/27/78:500000
[root@localhost tmp]# 
[root@localhost tmp]# awk -F: '/^'"$oldname"'/{$1='"$newname"'; print $0}' datafile 
+ awk -F: '/^Ellie Main/{$1=Eleanor Quigley; print $0}' datafile
 408-298-7732 934 La Barbara Dr. , San Jose, CA 02/27/78 500000
[root@localhost tmp]# 

可以看到,$oldname 变量替换的结果是正确的,但是 $newname 变量替换的结果带有空白符,导致 $1=Eleanor Quigley 出现语法错误,应该使得 $1=“Eleanor Quigley” 才是正确执行。

[root@localhost tmp]# 
[root@localhost tmp]# cat datafile 
Ellie Main:408-298-7732:934 La Barbara Dr. , San Jose, CA:02/27/78:500000
[root@localhost tmp]# 
[root@localhost tmp]# awk -F: '/^'"$oldname"'/{$1="'"$newname"'"; print $0}' datafile    
+ awk -F: '/^Ellie Main/{$1="Eleanor Quigley"; print $0}' datafile
Eleanor Quigley 408-298-7732 934 La Barbara Dr. , San Jose, CA 02/27/78 500000
[root@localhost tmp]#

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值