bash - 为什么 type -a 执行“$(printf 'a')”并且是否可以取回原始别名?

bash - 为什么 type -a 执行“$(printf 'a')”并且是否可以取回原始别名?

在我问这个之后问题2 天前,我决定在我的 ~/.bashrc 中创建这个别名:

别名 catvu="LC_ALL=C sed \"$(printf 's/[^\t -\176\200-\377]/^&/g')\"|LC_ALL=C tr '\0-\10\ 13-\37\177''@-HK-_?'"

如果我通过 grep 查询别名,输出是正确的:

[xiaobai@xiaobai note]$ grep catvu ~/.bashrc
alias catvu="LC_ALL=C sed \"$(printf 's/[^\t -\176\200-\377]/^&/g')\"|LC_ALL=C tr '\0-\10\13-\37\177' '@-HK-_?'"

但输出略有不同,其中 printf 消失了:

[xiaobai@xiaobai note]$ type -a catvu
catvu is aliased to `LC_ALL=C sed "s/[^  -~�-�]/^&/g"|LC_ALL=C tr '\0-\10\13-\37\177' '@-HK-_?''

我尝试与猫-V:

[xiaobai@xiaobai note]$ type -a catvu | cat -v
catvu is aliased to `LC_ALL=C sed "s/[^  -~M-^@-M-^?]/^&/g"|LC_ALL=C tr '\0-\10\13-\37\177' '@-HK-_?''

十六进制转储-C:

[xiaobai@xiaobai note]$ type -a catvu | hexdump -C                                                                                                    
00000000  63 61 74 76 75 20 69 73  20 61 6c 69 61 73 65 64  |catvu is aliased|                                                                        
00000010  20 74 6f 20 60 4c 43 5f  41 4c 4c 3d 43 20 73 65  | to `LC_ALL=C se|                                                                        
00000020  64 20 22 73 2f 5b 5e 09  20 2d 7e 80 2d ff 5d 2f  |d "s/[^. -~.-.]/|                                                                        
00000030  5e 26 2f 67 22 7c 4c 43  5f 41 4c 4c 3d 43 20 74  |^&/g"|LC_ALL=C t|                                                                        
00000040  72 20 27 5c 30 2d 5c 31  30 5c 31 33 2d 5c 33 37  |r '\0-\10\13-\37|                                                                        
00000050  5c 31 37 37 27 20 27 40  2d 48 4b 2d 5f 3f 27 27  |\177' '@-HK-_?''|                                                                        
00000060  0a                                                |.|                                                                                       
00000061                                                                                                                                              
[xiaobai@xiaobai note]$ 

我缩小范围:

alias catvu2="$(printf 'a')"

输出变为:

[xiaobai@xiaobai note]$ type -a catvu2
catvu2 is aliased to `a'
[xiaobai@xiaobai note]$ 

哪个命令相同:

[xiaobai@xiaobai note]$ which catvu2
alias catvu2='a'
[xiaobai@xiaobai note]$ 

所以我的问题是为什么$(打印函数被执行了,我怎样才能得到原始原始字符串通过使用A型命令?

答案1

请注意,命令替换$(printf ....)位于双引号内:

alias catvu="LC_ALL=C sed \"$(printf 's/[^\t -\176\200-\377]/^&/g')\"|LC_ALL=C tr '\0-\10\13-\37\177' '@-HK-_?'"

$(printf ....)因此,执行命令替换别名是定义的

让我们举一个更简单的例子来说明同样的观点。让我们定义一个 shell 变量d

$ d="a b $(printf "%s" hello) c d"

现在,让我们来declare -p看看到底d是如何定义的:

$ declare -p d
declare -- d="a b hello c d"

同样,就像别名一样,在定义 shell 变量之前首先执行命令替换。

延迟执行printf

考虑:

$ alias abc='echo a b $(printf "%s" Hello) c d'

在上面,$(printf ...)是在单引号内。因此,命令替换为不是执行并且该printf语句是别名定义的一部分:

$ alias abc
alias abc='echo a b $(printf "%s" Hello) c d'

printf但是,当我们运行别名时会执行:

$ abc
a b Hello c d

答案2

尝试以下两个命令,看看会得到什么:

echo "$(printf 'a')"
echo '$(printf 'a')'

本质上,单引号将为您提供“原始原始字符串”,而双引号中的任何内容都将在分配给您的别名之前进行评估。

但是,您可能会注意到第二个命令的输出中缺少“a”周围的单引号。您必须仔细检查并跟踪您的报价,以避免丢失报价 - 就像您必须使用\"内部双引号一样,只是您不能在单引号内执行此操作。你必须使用类似的东西'\''

因此,我建议使用函数而不是别名,这样您就不必担心保护您的引号。这还允许您在命令行上给出文件名,而您的别名仅在您将某些内容通过管道传输到其中时才有效。

catvu () {
    LC_ALL=C sed "$(printf 's/[^\t -\176\200-\377]/^&/g')" "$@" | LC_ALL=C tr '\0-\10\13-\37\177' '@-HK-_?'
}

相关内容