[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
(cross post from bug-make) A complete example of quoting an arbitrary va
From: |
Poor Yorick |
Subject: |
(cross post from bug-make) A complete example of quoting an arbitrary value as a word in a shell script |
Date: |
Wed, 26 Oct 2022 21:57:55 +0300 |
Hi,
Posted this already to bug-make, but should have posted here in the
first place.
Maybe this has been done before, but I couldn't find it on the nets.
The
example Makefile below illustrates how to quote an arbitrary value as a
word in
a shell script. One use case for this is to evaluate a value as a
complete
shell script, similar to .ONESHELL but allowing all shell features,
including
heredocs. Surely there are many use cases.
The process to quote a value is:
Escape single quotes.
Quote newlines.
If $'\n' notation is available in the shell, use that.
Otherwise, enclose newlines in single quotes.
Enclose everything else in Single quotes.
In a Posix shell, where $'...' is not available it's tricky because
newlines
are stripped from the result of a command subsitution, so $(printf '\n')
also
does not work directly. The workaround used below is to have printf
produce both the
a newline and the single quotes that enclose it. For example, the
following script,
echo 'hello'
echo goodbye
is transformed into,
'echo '\''hello'\''
''echo goodbye'
and can be evaluated>
eval 'echo '\''hello'\''
''echo goodbye'
Here is an example Makefile>
<code>
#SHELL = bash
SHELL = dash
define newline
endef
define script0
echo 'line one
line two
'
cat <<-'eof'
line three
line four
eof
endef
ifeq ($(shell printf '%s' $$'\n'),$$\n)
$(warning posix shell quoting)
# this is more difficult
# Assume $(1) will be enclosed in single quotes, break each single
quote
# out of the enclosing quotes, and replace it with an escaped single
quote.
shquotequote = $(subst ','\'',$(1))
# Assume $(1) will be enclosed in single quotes, break each newline out
of
# the enclosing quotes, and replace it with a command substitution that
# produces a newline enclosed in single quotes. This results in some
# syntactically unnecessary quoting, but avoids having newline
characters
# stripped from the result of the command substitution.
shnlquote = $(subst $(newline),'$$(printf "'\n'")','$(call
shquotequote,$(1))')
# Just print the string using printf, using eval to perform the newline
# command subsitutions and strip away the extra layer of quoting added
# shnlquote and shquotequote.
shquote = "$$(eval printf '%s' "$(call shnlquote,$(1))")"
else
$(warning direct shell quoting)
# this is easy...
# Replace each single quote with a quoted single quote,
# call shnlquoe to deal with newlines
# Add the first and last single quotes
shquote = '$(call shnlquote,$(subst ','"'"',$(1)))'
# replace each newline character with a
shnlquote = $(subst $(newline),'$$'\n'',$(1))
endif
script0:
eval $(call shquote,$(script0))
.PHONY: script0
printscript0:
printf '%s\n' $(call shquote,$(script0))
.PHONY: printscript0
</code>
The following quoting implementation also works, but was discarded in
favor of
the one above:
<code>
# In a posix shell $'\n' is not a valid representation of newline, and
it
# isn't possible to produce a newline using command substitution because
# newlines are stripped off the result. Instead, use printf to replace
a
# newline character surrounded by double quotes.
shnlquote = $(subst $(newline),'\'$$(printf '"\n"')\'',$(1))
# Because each newline character is enclosed in an additional layer of
# quotes, enclose everything else in an additional layer of quotes too.
shquotequote = \''$(call shnlquote,$(subst ','\'\"\'\"\'',$(1)))'\'
# Finally, Use printf again to remove the extra layer of quoting, with
the main
# effect here not being printf, but the quote removal that the shell
# performs as it reads the script.
shquote = "$$(printf '%s\n' $(call shquotequote,$(1)))"
</code>
Enjoy!
--
Yorick
To support this and further work, make a payment to the following
Ethereum
address (Mainnet, ImmutableX Layer 2, or Loopring Layer 2 only):
0x0b5049C148b00a216B29641ab16953b6060Ef8A6
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- (cross post from bug-make) A complete example of quoting an arbitrary value as a word in a shell script,
Poor Yorick <=