在 BASH 参数扩展中用波浪号“~”替换子字符串时的行为不一致
Inconsistent behavior when replacing substring with tilde "~" in a BASH parameter expansion
我在 BASH 跨几个不同服务器的参数扩展中遇到了一些奇怪的不一致行为,同时尝试编写一个快速函数。
在某些版本的 BASH 上,要在子字符串替换中使用波浪号,必须对波浪号进行转义,否则它将重新扩展到主目录:
foo=~/data # ~ is expanded to $HOME
bar1="${foo/#$HOME/\~}" # returns ~/data
bar2="${foo/#$HOME/"~"}" # returns ~/data
bar3="${foo/#$HOME/~}" # returns /home/user/data
而在其他系统上,它不会被重新扩展,并且尝试转义波浪号会将原义转义字符添加到字符串中:
foo=~/data # ~ is expanded to $HOME
bar1="${foo/#$HOME/\~}" # returns \~/data
bar2="${foo/#$HOME/"~"}" # returns "~"/data
bar3="${foo/#$HOME/~}" # returns ~/data
请注意,我的目标是插入文字字符串“~”。
不需要转义的BASH版本在这里:
GNU bash, version 4.4.12(1)-release (x86_64-apple-darwin16.3.0)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
GNU bash, version 4.2.37(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
需要转义 的 BASH 版本在这里:
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
所以这是怎么回事?
eval
是一种选择。
eval bar1="${foo/#$HOME/\~}"
eval bar2="${foo/#$HOME/"~"}"
eval bar3="${foo/#$HOME/~}"
现在所有三种情况都得到 /home/foo/data
。
@iBug 发布了如何确保我们始终扩展波浪号。为确保我们始终在任何系统上获得波浪字符本身,请使用:
bar="${foo/#$HOME/~}" # returns ~/data or /home/user/data
[[ "$bar" =~ "$HOME"* ]] && bar="${foo/#$HOME/\~}"
仅当原始非转义波浪线被扩展时,才用转义波浪线重做替换。
我在 BASH 跨几个不同服务器的参数扩展中遇到了一些奇怪的不一致行为,同时尝试编写一个快速函数。
在某些版本的 BASH 上,要在子字符串替换中使用波浪号,必须对波浪号进行转义,否则它将重新扩展到主目录:
foo=~/data # ~ is expanded to $HOME
bar1="${foo/#$HOME/\~}" # returns ~/data
bar2="${foo/#$HOME/"~"}" # returns ~/data
bar3="${foo/#$HOME/~}" # returns /home/user/data
而在其他系统上,它不会被重新扩展,并且尝试转义波浪号会将原义转义字符添加到字符串中:
foo=~/data # ~ is expanded to $HOME
bar1="${foo/#$HOME/\~}" # returns \~/data
bar2="${foo/#$HOME/"~"}" # returns "~"/data
bar3="${foo/#$HOME/~}" # returns ~/data
请注意,我的目标是插入文字字符串“~”。
不需要转义的BASH版本在这里:
GNU bash, version 4.4.12(1)-release (x86_64-apple-darwin16.3.0)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
GNU bash, version 4.2.37(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
需要转义 的 BASH 版本在这里:
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
所以这是怎么回事?
eval
是一种选择。
eval bar1="${foo/#$HOME/\~}"
eval bar2="${foo/#$HOME/"~"}"
eval bar3="${foo/#$HOME/~}"
现在所有三种情况都得到 /home/foo/data
。
@iBug 发布了如何确保我们始终扩展波浪号。为确保我们始终在任何系统上获得波浪字符本身,请使用:
bar="${foo/#$HOME/~}" # returns ~/data or /home/user/data
[[ "$bar" =~ "$HOME"* ]] && bar="${foo/#$HOME/\~}"
仅当原始非转义波浪线被扩展时,才用转义波浪线重做替换。