查看 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 将特殊字符 # 解释为注释开始字符,因此它被打印出来。
打印变量确切内容的唯一正确方法是引用它。
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
所以我在下面遇到了一个问题,但我的问题更笼统——如何查看 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 将特殊字符 # 解释为注释开始字符,因此它被打印出来。
打印变量确切内容的唯一正确方法是引用它。
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