Name variable from string in Dash - sh

I need to set a variable name from a string.
Here is my code :
var="cle"
value="1"
printf -v $var "$value"
echo $cle
I want it to output "1"
I am getting:
illegal option "-v"
I tried to replace printf by the declare function but, dash doesn't include it either.

The only way to do this in dash is to use eval, but that is risky unless you know that your string is a valid identifier.
expr "$var" : '[_[:alpha:]][_[:alnum:]]\{0,\}$' && eval "$var=$value"
The call to expr fails (or at least, is intended to fail) if the value of var is anything other than a single valid identifier.

Related

Not getting exact value in Variable becasue value contains $ char

I have a string "/Name Pa$Name#my"
$myname = GetContent("Name")
GetContent is only gets the key and gives the value for that Key. I cannot modify the GetContent function.
If i execute above, as "Pa$Name#my" contains the '$' char, $myname gives me value as "Pa#my" and ignores "$Name" content.
What can i do to get the $myname = Pa$Name#my ? Can i append some special characters while assigning the $myname variable.
As far as I understand your trouble comes from the fact that the var $name does not exist. You can use single qotes if you don't want Powershell to look for vars :
$a = 'Pa$Name#my'

For what input and arguments will perl split give the result (""), if ever?

To me, it looks like perl split can never give the result (""), i.e. a single-element list whose single element is the empty string. No matter what -- any input, any arguments to split. Can anyone show otherwise? And if not, is this a feature or a bug?
I wanted split to be able to for consistency, but alas:
Note that splitting an EXPR that evaluates to the empty string always
produces zero fields, regardless of the LIMIT specified.
http://perldoc.perl.org/functions/split.html
E.g.:
$ echo ""|perl -ne 'chomp;print 0+split/x/,$_,-1'
0
$ echo "x"|perl -ne 'chomp;print 0+split/x/,$_,-1'
2
$ echo "xx"|perl -ne 'chomp;print 0+split/x/,$_,-1'
3
$ echo "xxx"|perl -ne 'chomp;print 0+split/x/,$_,-1'
4
And if not, is this a feature or a bug?
Not returning an empty string is not a bug. As per the documentation,
Note that splitting an EXPR that evaluates to the empty string always produces zero fields, regardless of the LIMIT specified.
Can anyone show otherwise?
It's highly unlikely that anyone will be able to find an input for which split return an empty string when it's documented to never return an empty string.
It sounds like you want a list of one item when the input is an empty string, so
length($_) ? split(..., $_, -1) : ""
This is a tentative answer to my own question, pending any further information/correction that may come from others:
There are no inputs and arguments to perl split for which the result will ever be a single-element list containing the empty string.
In order to get a result consistent with the promises 1) "size of result will always be one more than the number of separators (regexp matches)" and 2) "if there are no separators, the result will always be a single-element list whose element is the whole original string", what would normally be a clean function call expression
split /.../
instead needs to be wrapped as follows, including an additional auxiliary array:
#s = split /.../, $_, -1 or push #s, "";
and then #s used where the split /.../ normally would have been.
E.g.:
$ echo ""|perl -ne 'chomp;#s=split/x/,$_,-1 or push #s,"";print 0+#s'
1
$ echo "x"|perl -ne 'chomp;#s=split/x/,$_,-1 or push #s,"";print 0+#s'
2
$ echo "xx"|perl -ne 'chomp;#s=split/x/,$_,-1 or push #s,"";print 0+#s'
3
$ echo "xxx"|perl -ne 'chomp;#s=split/x/,$_,-1 or push #s,"";print 0+#s'
4
Or, alternatively, any code using bare split /.../ and relying on either of the above "promises" needs to be put inside a guard if (length) {...} and the case of length==0 handled in separate code.

Perl variable interpolation in string and substitution at the same time

I have the silly question
given($num){
...
when($num >= 13 and $num <= 99) return "$num_in_eng{$num}teen" =~ s/tt/t/;
...
}
but it gave an error
"Can't modify the string in substitution near s/tt/t/g;
$_ = "$num_in_eng{$num}teen";
s/tt/t/;
This work fine
You can't assign to a string literal. It wouldn't make sense to say "foo" = "bar" either.
If you try to use the match operator =~ with a substitution (for example, a regex term starting with s, like in s/foo/bar/), the result of the substitution will be assigned to the left hand side. If that is a literal, you will get this error.
It does not matter that you actually used string interpolation to create the literal.
Using a local variable and substituting its contents like you have shown the right solution. You can also use a named variable instead of the "default" $_:
$foo = whatever();
$foo =~ s/tt/t/;
return $foo;

Valid variable name in Template Toolkit

I'm using perl and Template::Toolkit to generate some texts. In the perl program, I defined a hash ref such as
my $user = { "user-name" => "John" };
and passed it to Template::Toolkit in order to generate John with the template file. In the template file I wrote
[% user-name %]
But unfortunately, user-name seemed to be recognized as a subtraction expression (user minus name) by Template::Toolkit.
To confirm my suspicions, I looked into the manual of Template::Toolkit to find the valid tokens to use in the variable name but found nothing.
So my questions are:
Could you give the list of valid tokens for variable names of
Template::Toolkit?
Could I use some "escaping method" in the template file so that the
variable name in perl program could remain user-name?
Generally, a valid perl variable is a valid template toolkit variable.
The variable should always start with a non numeric, non symbolic character [ A to Z and _ ]
Variable can only contain a to z, 0 to 9 and _ (underscore) characters.
Variable names cannot contain special symbols (it includes '-' symbol ).
Eg:
$user-name is not a valid perl variable. but
$user_name is valid.
Here is what perl interpreter throws for your code
$ my $user = { user-name => "John" };
Compile error: Bareword "user" not allowed while "strict subs" in use at (eval 294) line 5.
If you really want to use 'user-name' then you should define like this
$ my $user = { "user-name" => "John" };
$ my $data = { user => $user };
And you should access it in your tt2 file like this:
[% user.item('user-name') %]

How can I escape an arbitrary string for use as a command line argument in Bash?

I have a list of strings and I want to pass those strings as arguments in a single Bash command line call. For simple alphanumeric strings it suffices to just pass them verbatim:
> script.pl foo bar baz yes no
foo
bar
baz
yes
no
I understand that if an argument contains spaces or backslashes or double-quotes, I need to backslash-escape the double-quotes and backslashes, and then double-quote the argument.
> script.pl foo bar baz "\"yes\"\\\"no\""
foo
bar
baz
"yes"\"no"
But when an argument contains an exclamation mark, this happens:
> script.pl !foo
-bash: !foo: event not found
Double quoting doesn't work:
> script.pl "!foo"
-bash: !foo: event not found
Nor does backslash-escaping (notice how the literal backslash is present in the output):
> script.pl "\!foo"
\!foo
I don't know much about Bash yet but I know that there are other special characters which do similar things. What is the general procedure for safely escaping an arbitrary string for use as a command line argument in Bash? Let's assume the string can be of arbitrary length and contain arbitrary combinations of special characters. I would like an escape() subroutine that I can use as below (Perl example):
$cmd = join " ", map { escape($_); } #args;
Here are some more example strings which should be safely escaped by this function (I know some of these look Windows-like, that's deliberate):
yes
no
Hello, world [string with a comma and space in it]
C:\Program Files\ [path with backslashes and a space in it]
" [i.e. a double-quote]
\ [backslash]
\\ [two backslashes]
\\\ [three backslashes]
\\\\ [four backslashes]
\\\\\ [five backslashes]
"\ [double-quote, backslash]
"\T [double-quote, backslash, T]
"\\T [double-quote, backslash, backslash, T]
!1
!A
"!\/'" [double-quote, exclamation, backslash, forward slash, apostrophe, double quote]
"Jeff's!" [double-quote, J, e, f, f, apostrophe, s, exclamation, double quote]
$PATH
%PATH%
&
<>|&^
*#$$A$##?-_
EDIT:
Would this do the trick? Escape every unusual character with a backslash, and omit single or double quotes. (Example is in Perl but any language can do this)
sub escape {
$_[0] =~ s/([^a-zA-Z0-9_])/\\$1/g;
return $_[0];
}
If you want to securely quote anything for Bash, you can use its built-in printf %q formatting:
cat strings.txt:
yes
no
Hello, world
C:\Program Files\
"
\
\\
\\\
\\\\
\\\\\
"\
"\T
"\\T
!1
!A
"!\/'"
"Jeff's!"
$PATH
%PATH%
&
<>|&^
*#$$A$##?-_
cat quote.sh:
#!/bin/bash
while IFS= read -r string
do
printf '%q\n' "$string"
done < strings.txt
./quote.sh:
yes
no
Hello\,\ world
C:\\Program\ Files\\
\"
\\
\\\\
\\\\\\
\\\\\\\\
\\\\\\\\\\
\"\\
\"\\T
\"\\\\T
\!1
\!A
\"\!\\/\'\"
\"Jeff\'s\!\"
\$PATH
%PATH%
\&
\<\>\|\&\^
\*#\$\$A\$##\?-_
These strings can be copied verbatim to for example echo to output the original strings in strings.txt.
What is the general procedure for safely escaping an arbitrary string for use as a command line argument in Bash?
Replace every occurrence of ' with '\'', then put ' at the beginning and end.
Every character except for a single quote can be used verbatim in a single-quote-delimited string. There's no way to put a single quote inside a single-quote-delimited string, but that's easy enough to work around: end the string ('), then add a single quote by using a backslash to escape it (\'), then begin a new string (').
As far as I know, this will always work, with no exceptions.
You can use single quotes to escape strings for Bash. Note however this does not expand variables within quotes as double quotes do. In your example, the following should work:
script.pl '!foo'
From Perl, this depends on the function you are using to spawn the external process. For example, if you use the system function, you can pass arguments as parameters so there"s no need to escape them. Of course you"d still need to escape quotes for Perl:
system("/usr/bin/rm", "-fr", "/tmp/CGI_test", "/var/tmp/CGI");
sub text_to_shell_lit(_) {
return $_[0] if $_[0] =~ /^[a-zA-Z0-9_\-]+\z/;
my $s = $_[0];
$s =~ s/'/'\\''/g;
return "'$s'";
}
See this earlier post for an example.
Whenever you see you don't get the desired output, use the following method:
"""\special character"""
where special character may include ! " * ^ % $ # # ....
For instance, if you want to create a bash generating another bash file in which there is a string and you want to assign a value to that, you can have the following sample scenario:
Area="(1250,600),(1400,750)"
printf "SubArea="""\""""${Area}"""\""""\n" > test.sh
printf "echo """\$"""{SubArea}" >> test.sh
Then test.sh file will have the following code:
SubArea="(1250,600),(1400,750)"
echo ${SubArea}
As a reminder to have newline \n, we should use printf.
Bash interprets exclamation marks only in interactive mode.
You can prevent this by doing:
set +o histexpand
Inside double quotes you must escape dollar signs, double quotes, backslashes and I would say that's all.
This is not a complete answer, but I find it useful sometimes to combine two types of quote for a single string by concatenating them, for example echo "$HOME"'/foo!?.*' .
FWIW, I wrote this function that invokes a set of arguments using different credentials. The su command required serializing all the arguments, which required escaping them all, which I did with the printf idiom suggested above.
$ escape_args_then_call_as myname whoami
escape_args_then_call_as() {
local user=$1
shift
local -a args
for i in "$#"; do
args+=( $(printf %q "${i}") )
done
sudo su "${user}" -c "${args[*]}"
}