Shell基础脚本-引用

2021-11-14  本文已影响0人  Chris0Yang

引用就是将一个字符串用引号括起来。这样做是为了保护Shell/Shell脚本中被重新解释过或带扩展功能的特殊字符(如果一个字符带有其特殊意义而不仅仅是字面量的话,这个字符就能称为“特殊字符”。比如星号“*”就能表示 正则表达式 中的一个通配符

bash$ ls -l [Vv]*
-rw-rw-r--    1 bozo  bozo       324 Apr  2 15:05 VIEWDATA.BAT
-rw-rw-r--    1 bozo  bozo       507 May  4 14:25 vartrace.sh
-rw-rw-r--    1 bozo  bozo       539 Apr 14 17:11 viewdata.sh

bash$ ls -l '[Vv]*'
ls: [Vv]*: No such file or directory
可以看到,提示不存在该文件。
这里的'[Vv]*被当成了文件名。 
在日常沟通和写作中,当我们引用一个短语的时候,我们会将它单独隔开并赋予它特殊的意义
而在bash脚本中,当我们引用一个字符串,意味着保留它的字面量。

很多程序和公用代码会展开被引用字符串中的特殊字符。
引用的一个重用用途是保护Shell中的命令行参数,但仍然允许调用的程序扩展它

bash$ grep '[Ff]irst' *.txt
file1.txt:This is the first line of file1.txt.
file2.txt:This is the First line of file2.txt.

在所有.txt文件中找出包含first或者First字符串的行

注意,不加引号的 grep [Ff]irst *.txt 在Bash下也同样有效

引用也可以控制echo命令的断行符

bash$ echo $(ls -l)
total 8 -rw-rw-r-- 1 bo bo 13 Aug 21 12:57 t.sh -rw-rw-r-- 1 bo bo 78 Aug 21 12:57 u.sh


bash$ echo "$(ls -l)"
total 8
 -rw-rw-r--  1 bo bo  13 Aug 21 12:57 t.sh
 -rw-rw-r--  1 bo bo  78 Aug 21 12:57 u.sh

1、引用变量

引用变量时,通常建议将变量包含在双引号中。
因为这样可以防止除 $,` (反引号)和\(转义符)之外的其他特殊字符被重新解释。
在双引号中仍然可以使用$引用变量("$variable"),也就是将变量名替换为变量值
使用双引号可以防止字符串被分割
即使参数中拥有很多空白分隔符,被包在双引号中后依旧是算作单一字符。
List="one two three"

for a in $List     # 空白符将变量分成几个部分。
do
  echo "$a"
done
# one
# two
# three

echo "---"

for a in "$List"   # 在单一变量中保留所有空格。
do #     ^     ^
  echo "$a"
done
# one two three 

下面是一个更加复杂的例子:

variable1="a variable containing five words"
COMMAND This is $variable1    # 带上7个参数执行COMMAND命令:
# "This" "is" "a" "variable" "containing" "five" "words"

COMMAND "This is $variable1"  # 带上1个参数执行COMMAND命令:
# "This is a variable containing five words"


variable2=""    # 空值。

COMMAND  $variable2 $variable2 $variable2
                # 不带参数执行COMMAND命令。
COMMAND "$variable2" "$variable2" "$variable2"
                # 带上3个参数执行COMMAND命令。
COMMAND "$variable2 $variable2 $variable2"
                # 带上1个参数执行COMMAND命令(2空格)。

当字符分割或者保留空白符出现问题时,才需要在echo语句中用双引号包住参数

样例-1. 输出一些奇怪的变量

#!/bin/bash
# weirdvars.sh: 输出一些奇怪的变量

echo

var="'(]\\{}\$\""
echo $var        # '(]\{}$"
echo "$var"      # '(]\{}$"     没有任何区别。

echo

IFS='\'
echo $var        # '(] {}$"     \ 被转换成了空格,为什么?
echo "$var"      # '(]\{}$"

# 上面的例子由 Stephane Chazelas 提供。

echo

var2="\\\\\""
echo $var2       #   "
echo "$var2"     # \\"
echo
# 但是...var2="\\\\"" 不是合法的语句,为什么?
var3='\\\\'
echo "$var3"     # \\\\
# 强引用是可以的。


# ************************************************************ #
# 就像第一个例子展示的那样,嵌套引用是允许的。

echo "$(echo '"')"           # "
#    ^           ^


# 在有些时候这种方法非常有用。

var1="Two bits"
echo "\$var1 = "$var1""      # $var1 = Two bits
#    ^                ^

# 或者,可以像 Chris Hiestand 指出的那样:

if [[ "$(du "$My_File1")" -gt "$(du "$My_File2")" ]]
#     ^     ^         ^ ^     ^     ^         ^ ^
then
  ...
fi
# ************************************************************ #

单引号(' ')与双引号类似,但是在单引号中不能引用变量,因为 $ 不再具有特殊含义
在单引号中,除'之外的所有特殊字符都将会被直接按照字面意思解释,可以认为单引号(“全引用”)是双引号(“部分引用”)的一种更严格的形式

因为在单引号中转义符(\)都已经按照字面意思解释了
因此尝试在单引号中包含单引号将不会产生你所预期的结果
echo "Why can't I write 's between single quotes"

echo

# 可以采取迂回的方式。
echo 'Why can'\''t I write '"'"'s between single quotes'
#    |-------|  |----------|   |-----------------------|
# 由三个单引号引用的字符串,再加上转义以及双引号包住的单引号组成。

2、转义

转义是一种引用单字符的方法。通过在特殊字符前加上转义符 \ 来告诉shell按照字面意思去解释这个字符

需要注意的是,在一些特定的命令和工具
比如: echo 和 sed 中,转义字符通常会起到相反的效果,即可能会使得那些字符产生特殊含义

在 echo 与 sed 命令中,转义字符的特殊含义

( \n )

换行(line feed)

( \r )

回车(carriage return)

( \t )

水平制表符

( \v )

垂直制表符

( \b )

退格

( \a )

警报、响铃或闪烁

( \0xx )

ASCII码的八进制形式,等价于 0nn,其中 nn 是数字

在 $' ... ' 字符串扩展结构中可以通过转义八进制或十六进制的ASCII码形式给变量赋值
比如: quote=$'\042'

样例-2. 转义字符

#!/bin/bash
# escaped.sh: 转义字符

##############################################
### 首先让我们先看一下转义字符的基本用法。 ###
##############################################

# 转义新的一行。
# ------------

echo ""

echo "This will print
as two lines."
# This will print
# as two lines.

echo "This will print \
as one line."
# This will print as one line.

echo; echo

echo "============="


echo "\v\v\v\v"      # 按字面意思打印 \v\v\v\v
# 使用 echo 命令的 -e 选项来打印转义字符。
echo "============="
echo "VERTICAL TABS"
echo -e "\v\v\v\v"   # 打印四个垂直制表符。
echo "=============="

echo "QUOTATION MARK"
echo -e "\042"       # 打印 " (引号,八进制ASCII码为42)。
echo "=============="



# 使用 $'\X' 这样的形式后可以不需要加 -e 选项。

echo; echo "NEWLINE and (maybe) BEEP"
echo $'\n'           # 新的一行。
echo $'\a'           # 警报(响铃)。
                     # 根据不同的终端版本,也可能是闪屏。

# 我们之前介绍了 $'\nnn' 字符串扩展,而现在我们要看到的是...

# ============================================ #
# 自 Bash 第二个版本开始的 $'\nnn' 字符串扩展结构。
# ============================================ #

echo "Introducing the \$\' ... \' string-expansion construct . . . "
echo ". . . featuring more quotation marks."

echo $'\t \042 \t'   # 在制表符之间的引号。
# 需要注意的是 '\nnn' 是一个八进制的值。

# 字符串扩展同样适用于十六进制的值,格式是 $'\xhhh'。
echo $'\t \x22 \t'  # 在制表符之间的引号。
# 感谢 Greg Keraunen 指出这些。
# 在早期的 Bash 版本中允许使用 '\x022' 这样的形式。

echo


# 将 ASCII 码字符赋值给变量。
# -----------------------
quote=$'\042'        # 将 " 赋值给变量。
echo "$quote Quoted string $quote and this lies outside the quotes."

echo

# 连接多个 ASCII 码字符给变量。
triple_underline=$'\137\137\137'  # 137是 '_' ASCII码的八进制形式
echo "$triple_underline UNDERLINE $triple_underline"

echo

ABC=$'\101\102\103\010'           # 101,102,103是 A, B, C 
                                  # ASCII码的八进制形式。
echo $ABC

echo

escape=$'\033'                    # 033 是 ESC 的八进制形式
echo "\"escape\" echoes an $escape"
                                  # 没有可见输出

echo

exit 0

下面是一个更加复杂的例子:
样例-3. 检测键盘输入

#!/bin/bash
# 需要 Bash 版本高于4.2。

key="no value yet"
while true; do
  clear
  echo "Bash Extra Keys Demo. Keys to try:"
  echo
  echo "* Insert, Delete, Home, End, Page_Up and Page_Down"
  echo "* The four arrow keys"
  echo "* Tab, enter, escape, and space key"
  echo "* The letter and number keys, etc."
  echo
  echo "    d = show date/time"
  echo "    q = quit"
  echo "================================"
  echo

  # 将独立的Home键值转换为数字7上的Home键值:
  if [ "$key" = $'\x1b\x4f\x48' ]; then
   key=$'\x1b\x5b\x31\x7e'
   #   引用字符扩展结构。
  fi

  # 将独立的End键值转换为数字1上的End键值:
  if [ "$key" = $'\x1b\x4f\x46' ]; then
   key=$'\x1b\x5b\x34\x7e'
  fi

  case "$key" in
   $'\x1b\x5b\x32\x7e')  # 插入
    echo Insert Key
   ;;
   $'\x1b\x5b\x33\x7e')  # 删除
    echo Delete Key
   ;;
   $'\x1b\x5b\x31\x7e')  # 数字7上的Home键
    echo Home Key
   ;;
   $'\x1b\x5b\x34\x7e')  # 数字1上的End键
    echo End Key
   ;;
   $'\x1b\x5b\x35\x7e')  # 上翻页
    echo Page_Up
   ;;
   $'\x1b\x5b\x36\x7e')  # 下翻页
    echo Page_Down
   ;;
   $'\x1b\x5b\x41')  # 上箭头
    echo Up arrow
   ;;
   $'\x1b\x5b\x42')  # 下箭头
    echo Down arrow
   ;;
   $'\x1b\x5b\x43')  # 右箭头
    echo Right arrow
   ;;
   $'\x1b\x5b\x44')  # 左箭头
    echo Left arrow
   ;;
   $'\x09')  # 制表符
    echo Tab Key
   ;;
   $'\x0a')  # 回车
    echo Enter Key
   ;;
   $'\x1b')  # ESC
    echo Escape Key
   ;;
   $'\x20')  # 空格
    echo Space Key
   ;;
   d)
    date
   ;;
   q)
    echo Time to quit...
    echo
    exit 0
   ;;
   *)
    echo Your pressed: \'"$key"\'
   ;;
  esac

  echo
  echo "================================"

  unset K1 K2 K3
  read -s -N1 -p "Press a key: "
  K1="$REPLY"
  read -s -N2 -t 0.001
  K2="$REPLY"
  read -s -N1 -t 0.001
  K3="$REPLY"
  key="$K1$K2$K3"

done

exit $?

( " )

转义引号,指代自身

echo "Hello"                     # Hello
echo "\"Hello\" ... he said."    # "Hello" ... he said.

( $ )

转义美元符号(跟在 \$ 后的变量名将不会被引用)

echo "\$variable01"           # $variable01
echo "The book cost \$7.98."  # The book cost $7.98.

( \ )

转义反斜杠,指代自身

echo "\\"  # 结果是 \

# 然而...

echo "\"   # 在命令行中会出现第二行并提示输入。
           # 在脚本中会出错。

# 但是...

echo '\'   # 结果是 \
根据转义符所在的上下文(强引用、弱引用,命令替换或者在 here document)的不同
它的行为也会有所不同
                      #  简单转义与引用
echo \z               #  z
echo \\z              # \z
echo '\z'             # \z
ehco '\\z'            # \\z
echo "\z"             # \z
echo "\\z"            # \z

                      #  命令替换
echo `echo \z`        #  z
echo `echo \\z`       #  z
echo `echo \\\z`      # \z
echo `echo \\\\z`     # \z
echo `echo \\\\\\z`   # \z
echo `echo \\\\\\\z`  # \\z
echo `echo "\z"`      # \z
echo `echo "\\z"`     # \z

                      # Here Document
cat <<EOF
\z
EOF                   # \z

cat <<EOF
\\z
EOF                   # \z

含有转义字符的字符串可以赋值给变量,但是仅仅将单一的转义符赋值给变量是不可行的

variable=\
echo "$variable"
# 这样做会报如下错误:
# tesh.sh: : command not found
# 单独的转义符不能够赋值给变量。
# 
#  事实上,"\" 转义了换行,实际效果是:
#+ variable=echo "$variable"
#+ 这是一个非法的赋值方式。

variable=\
23skidoo
echo "$variable"        # 23skidoo
                        # 因为第二行是一个合法的赋值,因此不会报错。

variable=\ 
#        \^    转义符后有一个空格
echo "$variable"        # 空格

variable=\\
echo "$variable"        # \

variable=\\\
echo "$variable"
# 这样做会报如下错误:
# tesh.sh: \: command not found
#
#  第一个转义符转转义了第二个,但是第三个转义符仍旧转义的是换行,
#+ 跟开始的那个例子一样,因此会报错。

variable=\\\\
echo "$variable"        # \\
                        # 第二个和第四个转义符被转义了,因此可行。

转义空格能够避免在命令参数列表中的字符分割问题

file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"
# 将一系列文件作为命令的参数。

# 增加两个文件到列表中,并且列出整个表。
ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list

echo "-------------------------------------------------------------------------"

# 如果我们转义了这些空格会怎样?
ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
# 错误:因为转义了两个空格,因此前三个文件被连接成了一个参数传递给了 'ls -l'

转义符也提供一种可以撰写多行命令的方式。通常,每一行是一个命令,但是转义换行后命令就可以在下一行继续撰写

(cd /source/directory && tar cf - . ) | \
(cd /dest/directory && tar xpvf -)
# 回顾 Alan Cox 的目录树拷贝命令,但是把它拆成了两行。

# 或者你也可以:
tar cf - -C /source/directory . |
tar xpvf - -C /dest/directory
在脚本中,如果以 "|" 管道作为一行的结束字符,那么不需要加转义符  也可以写多行命令。
但是一个好的编程习惯就是在写多行命令的事后,无论什么情况都要在行尾加上转义符 \
echo "foo
bar"
#foo
#bar

echo

echo 'foo
bar'    # 没有区别。
#foo
#bar

echo

echo foo\
bar     # 转义换行。
#foobar

echo

echo "foo\
bar"     # 没有区别,在弱引用中,\ 转义符仍旧转义了换行。
#foobar

echo

echo 'foo\
bar'     # 在强引用中,\ 就按照字面意思来解释了。
#foo\
#bar
上一篇 下一篇

猜你喜欢

热点阅读