查看 bash 变量的确切内容? (hexdump 没有帮助)

See exact content of bash variable? (hexdump doesn't help)

所以我在下面遇到了一个问题,但我的问题更笼统——如何查看 bash 变量引用的内存的确切内容以了解它们不匹配的原因:

# [[ $c1 == $c ]] || echo nope
nope
# [[ $c1 == "bkup dt" ]] || echo nope
# [[ $c == "bkup dt" ]] || echo nope
nope
# hexdump -C <<<$c
00000000  62 6b 75 70 20 64 74 0a                           |bkup dt.|
00000008
# hexdump -C <<<$c1
00000000  62 6b 75 70 20 64 74 0a                           |bkup dt.|
00000008
# [ "$c1" = "$c" ] || echo nope
nope
# [ ! "$c1" = "$c" ] || echo nope

或者它看起来像一个错误?我可以重复这个问题:

$ cd /tmp
$ mkdir aaa
$ echo 2 > aaa/1
$ echo 2 > aaa/2
$ c=$(ls -A aaa)
$ [[ $c == $(echo $c) ]] || echo not equal
not equal
$ hexdump -C <<<$c
00000000  31 20 32 0a                                       |1 2.|
00000004
$ hexdump -C <<<$(echo $c)
00000000  31 20 32 0a                                       |1 2.|
00000004
$ c1="1 2"
$ [[ $c1 == $(echo $c1) ]] || echo not equal
$ [[ $c1 == $(echo $c) ]] || echo not equal
$ [[ $c1 == $c ]] || echo not equal
not equal

检查变量内容的最好方法是使用 declare -p:

$ c="1 2"
$ declare -p c
declare -- c="1 2"

请注意,您的测试是错误的,因为您在变量扩展中遗漏了引号!

看:

$ c="1  2" # with two spaces
$ declare -p c
declare -- c="1  2"
$ echo $c
1 2
$ echo "$c"
1  2
$ d=$(echo $c)
$ declare -p d
1 2

你必须引用每个变量扩展,除非你真的想对它们应用分词和路径名扩展!(通常,你当然不希望这样做发生)。

即使使用您的 hexdump 策略,您也需要引用:

$ c="1  2" # two spaces
$ hexdump <<< $c
00000000  31 20 32 0a                                       |1 2.|
00000004
$ hexdump <<< "$c"
00000000  31 20 20 32 0a                                    |1  2.|
00000005

您遇到的正是:

$ mkdir aaa; touch aaa/{1,2}
$ c=$(ls -A aaa)
$ declare -p c
declare -- c="1
2"
$ # see? there's a new line between the files.
$ echo $c
1 2
$ echo "$c"
1
2
$ # Use quotes!

有时 declare -p 不会正确显示空格。在这种情况下,您可以像这样使用 printf

$ c=$'    \n'
$ declare -p c
declare -- c="    
"
$ # there are spaces, but you can't see them
$ printf '%q\n' "$c"
$'    \n'

声明策略也很好,因为您也可以检查数组和函数:

$ a=( one two "three four" )
$ declare -p a
declare -a a='([0]="one" [1]="two" [2]="three four")'
$ declare -A h=( [one]=1 [two]=2 )
$ declare -p h
declare -A h='([one]="1" [two]="2" )'
$ f() { echo hello; } > somewhere > >(over the rainbow)
$ declare -pf f
f () 
{ 
    echo hello
} > somewhere 2> >(over the rainbow)
$ # You need also the -f switch to target functions

您还可以访问变量的标志:

$ declare -litux hello=world
$ declare -p hello
declare -itx hello="0"
$ # Have fun!

您没有看到变量的确切内容,因为您要求bash修改它。最基本的效果就在这条命令中:

$ echo hello # world
hello

$ echo "hello # world"
hello # world

引用阻止 shell 将特殊字符 # 解释为注释开始字符,因此它被打印出来。

read more here

打印变量确切内容的唯一正确方法是引用它。

a='a \b 
|c     d'   

上面,var a 得到一个精确的字符串,因为它是单引号。

$ echo "$a"
a \b 
|c     d

上面,var a 的值由于双引号而被精确复制。

一旦你未能引用变量扩展$a,你就会得到其他东西:

$ echo $a
a \b |c d

这是任何 shell 的一个非常基本的要求:引用。
并且有单引号、双引号和反斜杠引号。


在你的情况下,为了重现你所看到的,变量并不完全相等,它们是:

$ c='bkup dt
'

$ c1='bkup dt'

也就是说,与 c1 相比,c 末尾多了一个换行符

让我们重复您的命令,但要正确引用以查看每个变量的真实内容(请理解,在 [[…]] 中的变量大部分时间都可以不加引号使用)。

$ [[ $c1 == $c ]] || echo nope
nope

变量不相等。

$ [[ $c1 == "bkup dt" ]] || echo nope

变量c1正好等于bkup dt(无输出)。

$ [[ $c == "bkup dt" ]] || echo nope
nope

不,c 不完全是 "bkup dt"(它多了一个新行)。

$ hexdump -C <<<"$c"
00000000  62 6b 75 70 20 64 74 0a  0a                       |bkup dt..|
00000009

注意末尾的 两个 换行符,现在引用的变量通过添加来自 <<<.

的换行符正确地再现了它的内部值

$c1比较。

$ hexdump -C <<<"$c1"
00000000  62 6b 75 70 20 64 74 0a                           |bkup dt.|
00000009

只有一个换行符!!

也许更好的方法是使用:

$ printf '%s' "$c" | od -vAn -tx1c
 62  6b  75  70  20  64  74  0a
  b   k   u   p       d   t  \n

这里,变量里面的换行符显示的很清楚。

变量$c1没有显示:

$ printf '%s' "$c1" | od -vAn -tx1c
 62  6b  75  70  20  64  74
  b   k   u   p       d   t

命令声明也可以使用:

$ declare -p c
declare -- c="bkup dt
"

$ declare -p c1
declare -- c1="bkup dt"

作为 cat、sed 和其他一些:

$ echo "$c" | cat -vET
bkup dt$
$

$ echo "$c1" | cat -vET
bkup dt$

$  echo "$c" | sed -n l
bkup dt$
$

$  echo "$c1" | sed -n l
bkup dt$

只要您引用变量扩展并知道新行的样子。


你的第二个例子也有换行问题。

$ cd /tmp; mkdir aaa; echo 2 > aaa/1; echo 2 > aaa/2
$ c=$(ls -A aaa)
$ echo $c | od -vAn -tx1c
  31  20  32  0a
   1       2  \n

在这种情况下,c 似乎有一个 space,但它真正有的是:

$ echo "$c" | od -vAn -tx1c
  31  0a  32  0a
   1  \n   2  \n

一个换行

因此,回显也失败了(对于几个缺失的引号,两个都需要):

$ echo $(echo $c) | odc
  31  20  32  0a
   1       2  \n

$ echo "$(echo "$c")" | odc
  31  0a  32  0a
   1  \n   2  \n