This question already has an answer here:

After i ask this question 2 days ago, i decided to make this alias in my ~/.bashrc:

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

The output is correct if i query the alias by 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-_?'"

but the output is slightly different in which the printf is gone:

[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-_?''

i try with cat -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-_?''

and hexdump -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                                                |.|                                                                                       
[xiaobai@xiaobai note]$ 

i narrow down it:

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

And the output become:

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

which command is same:

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

So my question is why $(printf is get executed and how can i get the original raw string by using type -a command?

marked as duplicate by Michael Homer, Anthon, mdpc, jasonwryan, cuonglm May 11 '15 at 8:11

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • @MichaelHomer does this duplicated question answer my question, i.e. how to get the original raw string without look into ~/.bashrc ? – 林果皞 May 11 '15 at 5:08
  • "So my question is why $(printf is get executed". – Michael Homer May 11 '15 at 5:48
  • The answer to the second half is "you can't, because it doesn't exist any more because of part 1". – Michael Homer May 11 '15 at 5:48
  • The part 1 doesn't exclude the possibility for the bash to store another version of alias which was not implement command substitution. Might be it store inside the ram or somewhere else. Simple example, do Facebook transform your upload photo? Yes. Do Facebook store the original upload photo ? I'm not really sure. – 林果皞 May 11 '15 at 9:05

Try the following two commands and see what you get:

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

Essentially, single quotes will give you your "original raw string", while anything in double quotes will be evaluated before being assigned to your alias.

However, you might notice that the single quotes around 'a' are missing from the output of second command. You'll have to go through and track your quotes carefully to avoid dropping quotes -- much like you had to use \" for your internal double quotes, except you can't do that within single quotes. You'd have to use something like '\''.

For this reason, I'd recommend using a function instead of an alias, so you don't have to worry about protecting your quotes at all. This would also allow you to give a filename on the command line, whereas your alias only works if you pipe something into it.

catvu () {
    LC_ALL=C sed "$(printf 's/[^\t -\176\200-\377]/^&/g')" "$@" | LC_ALL=C tr '\0-\10\13-\37\177' '@-HK-_?'
  • I accepted this answer because using a function seems like the only (easy) way to protect original alias. And it's impossible to get back the original alias after command substitution. type -a doesn't do what i expected to see, though. – 林果皞 May 11 '15 at 5:40

Notice that the command substitution $(printf ....) is inside double-quotes:

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

Consequently, the command substitution $(printf ....) is executed before the alias is defined.

Let's take a simpler example that illustrates the same point. Let's define a shell variable d:

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

Now, let's use declare -p to see exactly how d was defined:

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

Again, just like for the alias, the command substitution is performed first, before the shell variable is defined.

Delaying the execution of printf


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

In the above, $(printf ...) is inside single-quotes. Consequently, the command substitution is not performed and the printf statement is part of the alias definition:

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

printf is, however, executed when we run the alias:

$ abc
a b Hello c d
  • So, there's no way to retrieve the original alias unless i check the ~/.bashrc ? – 林果皞 May 11 '15 at 5:05
  • 1
    @林果皞 As defined (inside double-quotes), yes, that is correct. Because, in this case, the printf is executed before the alias is defined, it is not part of the alias definition. – John1024 May 11 '15 at 5:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.