bash parameter transformation gives bad substitution - bash

I always get a bad substitution error when I use Parameter Transformation in a script
#!/usr/bin/env bash
abc=abc
echo ${abc#U}
result
line 3: ${abc#U}: bad substitution
However, it works fine in interactive mode
> abc=abc
> echo ${abc#U}
ABC
I'm aware of the
Bash: How to use operator parameter expansion ${parameter#operator}?
question but there is no answer.
How can I use parameter transformation in a script?

Need bash version 5.1 or greater

Related

Does bash think this argument is a command? [duplicate]

I am writing a shell script where I am setting few variables, whose value is the output of commands.
The errors I get are:
$ $tag_name="proddeploy-$(date +"%Y%m%d_%H%M")"
-bash: =proddeploy-20141003_0500: command not found
now, I did read other similar questions and based on it, I tried various things:
spliting command into two calls
$ $deploy_date=date +"%Y%m%d_%H%M"
bash: =date: command not found
$ $tag_name="proddeploy-$deploy_date"
bash: proddeploy- command not found
tried using backticks
$ $tag_name=`proddeploy-$(date +"%Y%m%d_%H%M")`
bash: proddeploy-20141003_1734: command not found
bash: =: command not found
tried using $()
$ $tag_name=$(proddeploy-$(date +"%Y%m%d_%H%M"))
bash: proddeploy-20141003_1735: command not found
bash: =: command not found
But in every case the command output is getting executed. how do I make it to stop executing command output and just store as a variable? I need this to work on ZSH and BASH.
You define variables with var=string or var=$(command).
So you have to remove the leading $ and any other signs around =:
tag_name="proddeploy-$(date +"%Y%m%d_%H%M")"
deploy_date=$(date +"%Y%m%d_%H%M")
^^ ^
From Command substitution:
The second form `COMMAND` is more or less obsolete for Bash, since it
has some trouble with nesting ("inner" backticks need to be escaped)
and escaping characters. Use $(COMMAND), it's also POSIX!
Also, $() allows you to nest, which may be handy.
The accepted answer shows corrected code, but does not clarify that one of your problems is accessing a variable (using $) while assigning it, which is illegal.
For example:
$foo=4
should be
foo=4
See the difference? foo is being assigned, so you should not use $foo, which is not foo but the value of foo.

Default values for failed backticks in bash in one command

I am trying to come up with a mechanism to assign a default value to a variable when a command fails. I can't seem to combine them with || or inserting $() inside the 2nd line.
How do I convert this to one command?
MY_COMMAND=$(which somefile)
MY_COMMAND=${MY_COMMAND:-"/usr/local/dev/branch/somefile"}
The following should work in Bash:
MY_VARIABLE="$(which thing || echo 'default')"
As this is using a subshell ($() syntax), there should be a complete command on each side of the || or symbol. So the second part should echo something.
On the opposite, ${} is used in parameter substitution, and the variable names can't be nested.

Arithmetic operation in bash script

I saw an expression a=($(cat)) which I am not able to understand from it's working mechanism perspective.
Functionally it takes input from the standard input and assigns it to variable a (which forms an array).
My understanding is , when shell executes the inner parenthesese it executes the cat command which brings the standard input, and when you type a few lines on the standard input and press CTRL+D it returns the lines to the outer parenthesese which then assign the lines to an array a.
My question is why this expression gives error when I remove the $ and write it as a=((cat)).
It is because $(..) is a command substitution syntax to run commands on. The cat in your example run in a sub-shell under this construct. Without it the command cat and ( are interpreted literally which the shell does not like
From the bash(1) - Linux man page
Command Substitution
Command substitution allows the output of a command to replace the command name. There are two forms:
$(command) (or) command
Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.
The arithmetic operator in bash is $((..)) which is not the syntax you are using in your example

Build a shell command in a string for later execution portably

I am trying to do the following command in bash and dash:
x="env PATH=\"$PATH:/dir with space\""
cmd="ls"
"$x" $cmd
This fails with
-bash: env PATH="/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/git/bin:/usr/local/go/bin:/dir
with space": No such file or directory
Note the following works:
env PATH="$PATH:/dir with space" $cmd
The reason I am assigning to variable x env, is because it is part of a larger command wrapper to $cmd, which is also a complicated variable.
It's more complex than the initial example. I have logic in setting these variables,, instead of repeating them each time. Eventually the invocation is as shown here:
path_value="$PATH"
invocation="env PATH=\"$path_value\" $other_val1 $other_val2"
base="python python_script.py --opt1=a,b,c script_args"
add_on="$base more_arg1 more_arg2"
"$invocation" $base
You can use shell array to store and reuse:
x=(env PATH="$PATH:/dir with space")
cmd="ls"
"${x[#]}" "$cmd"
anubhava's helpful array-based bash answer is the best choice if you can assume only bash (which appeared to be the case initially).
Given that you must also support dash, which almost exclusively supports POSIX sh features only, arrays are not an option.
Assuming that you fully control or trust the values that you use to build your command line in a string, you can use eval, which should generally be avoided:
path_value="$PATH"
invocation="env PATH=\"$path_value\" $other_val1 $other_val2"
base="python python_script.py --opt1=a,b,c script_args"
add_on="$base more_arg1 more_arg2"
eval "$invocation $add_on"

Unexpected behavior of the unix echo command

while writing some script i found the following issue ,
suppose
set x = "param[xyz]";
echo $x
the output was echo:No match
But when i do ,
echo "$x"
i got the output: param[xyz]
so echo is doing a two way substitution ,
Initially echo $x was converted to echo param[xyz] and then it tried to look for the param[xyz] value .
But Ideally it should have just printed the variable whatever provided to it .
Does this behavior is a valid use case?
echo does no substitution at all, it's the shell that does it. It depends on the shell you use, but it seems that you are using a shell of the family of c-shells. Shell expands variables in the command line, so the first step is to generate:
[csh] echo param[xyz]
and then the shell does file match expansion, but as there is no file that correspond to the pattern the shell answers that there is no match. The message is somehow misleading as the shell reminds you what "command" was concerned not that the command failed by itself.
In the second try, enclosing the variable inside " prevent the shell to do other expansion and the shell launch the command with the argument obtained after the first expansion.
There exists another prevention if you use ', the shell won't ever expand variables:
[csh] echo '$x'
$x
Please refer to shell documentation and especially about expansion.
Another experience to convince you is to try with an non existing command:
[csh] weirdo z*
weirdo: No match
which is different than an non existing command:
[csh] weirdo
weirdo: Command not found.
If you'd use other shell behavior would be different:
[bash] echo z*
z*
because that shell produces as the argument the string itself in the case file matching is not working.
With:
[zsh] echo z*
zsh: no matches found: z*
the behavior is much more similar to c-shells but the message is much more clear, the shell failed at matching.

Resources