why creating var in bash need no space? - bash

I tried creating a var to do some operation in k8s.
I intentionally put space between '=' and export and my cmd I want to shortcut.
Why is linux shell giving me error?
export do = '--dry-run=client -o yaml'
-bash: export: `=': not a valid identifier
-bash: export: `--dry-run=client -o yaml': not a valid identifier

The basic operation in shell is running a command. You do this by stating the name of the command, followed by an optional space-separated list of arguments. Every other bit of syntax follows from this.
export do = '--dry-run=client -o yaml' is just a call to the command export with 3 separate arguments. There is no assignment.
An assignment is a single word containing a single = that appears in an appropriate context. In the case of an export command, each of its argument is either:
A valid identifier, on which the export attribute is set
A valid assignment in the form name=value, in which case name receives the export attribute and the value value is assigned to the name.
As mentioned in the comments, it's better to use an array to store lists of arguments. (Note that you cannot export an array, nor do you likely need to export any name that's only going to be used in the shell, rather than looked for in the environment of a child process.)
do=(--dry-run=client -o yaml)
some_command "${do[#]}" # instead of some_command $do

The reason why this doesn't work has to do with backwards-compliance for bash. The syntax you use on the CLI is the same on your would use on a shell script; shell scripts will always take the first word on each line and interpret them as commands.
Everything separated by a space after the command name will be interpreted as a space-separated list of arguments. Let's look at a simpler version of your export, as it's essentially setting your defined variable as an environment variable:
# This will evaluate to the command 'do' being executed with '='
# and '--dry-run=client -o yaml' being evaluated as arguments
do = '--dry-run=client -o yaml'
In order to circumvent that, Bash needs your variable assignments to be made without spaces. This way bash can lookup for any equal signs in your command and interpret them as assignments instead.
# Assigns the string '--dry-run=client -o ymal' to the variable 'do'
do='--dry-run=client -o yaml'
Since export just takes a variable assignment and turns it into a global environment variable, you need to follow the same convention:
export do='--dry-run=client -o yaml'

Export works like every unix program:
program agr1 arg2
The args are separated via spaces, so in your case the args are:
arg1: "do"
arg2: "="
arg3: "'--dry-run=client -o yaml'"
and export can't handle this, because it is meaningless.
But if you try this:
export a='echo a' b='echo b'
the args will be:
arg1: "a='echo a'"
arg2: "b='echo b'"
and export knows this syntax.

Related

Meaning of single character commands preceded by a hyphen [duplicate]

What are the differences between these terms: "option", "argument", and "parameter"? In man pages these terms often seem to be used interchangeably.
A command is split into an array of strings named arguments. Argument 0 is (normally) the command name, argument 1, the first element following the command, and so on. These arguments are sometimes called positional parameters.
$ ls -la /tmp /var/tmp
arg0 = ls
arg1 = -la
arg2 = /tmp
arg3 = /var/tmp
An option is a documented1 type of argument modifying the behavior of a command, e.g. -l commonly means "long", -v verbose. -lv are two options combined in a single argument. There are also long options like --verbose (see also Using getopts to process long and short command line options). As their name suggests, options are usually optional. There are however some commands with paradoxical "mandatory options".
$ ls -la /tmp /var/tmp
option1= -l
option2= -a
A parameter is an argument that provides information to either the command or one of its options, e.g. in -o file, file is the parameter of the -o option. Unlike options, whose possible values are hard coded in programs, parameters are usually not, so the user is free to use whatever string suits his/her needs. Should you need to pass a parameter that looks like an option but shouldn't be interpreted as such, you can separate it from the beginning of the command line with a double dash: --2.
$ ls -la /tmp /var/tmp
parameter1= /tmp
parameter2= /var/tmp
$ ls -l -- -a
option1 = -l
parameter1 = -a
A shell parameter is anything that store a value in the context of the shell. This includes positional parameters (e.g. $1, $2...), variables (e.g. $foo, $bar...) and special character ones (e.g. $#)
Finally, there are subcommands, also known as functions / (low-level) commands, which are used with "metacommands" that embed multiple separate commands, like busybox, git, apt-get, openssl, and the likes. With them, you might have global options preceeding the subcommand, and subcommand specific options that follow the subcommand. Unlike parameters, the list of possible subcommands is hardcoded in the command itself. e.g.:
$ busybox ls -l
command = busybox
subcommand = ls
subcommand option1 = -l
$ git --git-dir=a.git --work-tree=b -C c status -s
command = git
command option1 = --git-dir=a.git
command option2 = --work-tree=b
command option3 = -C c
subcommand = status
subcommand option1 = -s
Note that some commands like test, tar, dd and find have more complex argument parsing syntax than the ones described previously and can have some or all of their arguments parsed as expressions, operands, keys and similar command specific components.
Note also that optional variable assignments and redirections, despite being processed by the shell for tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal like other command line parameters are not taken into account in my reply because they have disappeared when the command is actually called and passed its arguments.
1 I should have written usually documented because of course, undocumented options are still options.
2 The double dash feature need to be implemented by the program though.
The man page for a typical Unix command often uses the terms argument, option and parameter. At the lowest level, we have argument and everything is an argument, including the (filesystem path to the) command itself.
In a shell script you access arguments using the special variables $0 .. $n. Other languages have similar ways to access them (commonly through an array with a name like argv).
Arguments may be interpreted as options if you wish. How this is done is implementation-specific. You can either roll your own, for exampe a shell (such as bash) script can use provided getopts or getopt commands.
These typically define an option as an argument beginning with a hyphen (-) and some options may use proceeding arguments as its parameters. More capable parsers (e.g getopt) support mixing short-form (-h) and long-form (--help) options.
Typically, most options take zero or one parameter. Such parameters are also sometimes called values.
The supported options are coded in the program code (e.g in the invocation of getopts within a shell script). Any remaining arguments after the options have been consumed are commonly called positional parameters when the order in which they are given is significant (this is in contrast to options which usually can be given in any order).
Again, the script defines what the positional parameters are by how it consumes and uses them.
So a typical command
$ ls -I README -l foo 'bar car' baz
has seven arguments: /usr/bin/ls, -I, README, -l, foo, bar car, and baz accessible as $0 thru $6. The -l and -I are interpreted as options, the latter having a parameter (or value) of README. What remains are positional parameters (foo, bar car and baz).
Option parsing may alter the argument list by removing those it consumes (e.g using shift or set) so that only the positional parameters remain and are thereafter accessible as $1 .. $n.
Since the question is tagged "bash", I looked for relevant sections in the Bash manual. I list these as quoted passages below together with my own one sentence summaries.
Arguments
Everything following the command is an argument.
A simple shell command such as echo a b c consists of the command itself followed by arguments, separated by spaces.
A simple command is the kind of command encountered most often. It’s just a sequence of words separated by blanks, terminated by one of the shell’s control operators (see Definitions). The first word generally specifies a command to be executed, with the rest of the words being that command’s arguments.
Parameters
Arguments are referred to as parameters during function execution.
When a function is executed, the arguments to the function become the positional parameters during its execution
A parameter is an entity that stores values. It can be a name, a number, or one of the special characters listed below. A variable is a parameter denoted by a name.
A positional parameter is a parameter denoted by one or more digits, other than the single digit 0. Positional parameters are assigned from the shell’s arguments when it is invoked, and may be reassigned using the set builtin command. Positional parameter N may be referenced as ${N}, or as $N when N consists of a single digit.
Options
There is no dedicated section to defining what an option is, but they are referred to as hyphen-prefixed characters throughout the manual.
The -p option changes the output format to that specified by POSIX

Why does `export` fail on bad substitutions but not command failures?

It’s well known that export masks the return value of command
substitutions in its variable assignments. But, interestingly,
export does not mask the return value of failed substitutions:
$ (set -eu; export FOO="$(bad_command)"); echo $?
bash: bad_command: command not found
0
$ (set -eu; export FOO="${bad_variable}"); echo $?
bash: bad_variable: unbound variable
1
$ (set -eu; export FOO="${}"); echo $? # bad substitution
bash: ${}: bad substitution
1
(Similar behavior in dash.)
What part of the specification indicates that failure of command
substitution does not propagate through export, but failure of
parameter expansion does?
Relevant sections from man bash (GNU Bash 4.4):
set -u
Treat unset variables and parameters other than the
special parameters "#" and "*" as an error when performing parameter
expansion. If expansion is attempted on an unset variable or
parameter, the shell prints an error message, and, if not interactive,
exits with a non-zero status.
and
export [-fn] [name[=word]] ...
export -p
The supplied names are marked for automatic export to the
environment of subsequently executed commands. If the -f option is
given, the names refer to functions. If no names are given, or if the
-p option is supplied, a list of names of all exported variables is
printed. The -n option causes the export property to be removed from
each name. If a variable name is followed by =word, the value of
the variable is set to word. export returns an exit status of 0
unless an invalid option is encountered, one of the names is not a
valid shell variable name, or -f is supplied with a name that is
not a function.
—I don’t see anything here that would distinguish between the two cases.
In particular, export just says that the value of the variable “is set
to” word, which suggests that it goes through the normal expansion
process (which it does) without special treatment.
POSIX specification references:
Shell Command Language
The export special builtin
The set special builtin
Neither case is really up to export
A command substitution just becomes text (empty or not) in a command's argument array. The Unix process model does not have any mechanism for relaying whether the text came from a program, or whether that program was successful.
This means that it's not possible to write an external command that behaves differently when you run foo var="$(true)" vs foo var="$(false)" vs foo var="" and shell builtins like export traditionally follow the same behavior for ease of implementation.
With set -u and unset variables, the command never runs at all. The shell simply skips execution if it encounters this condition while building the argument array, and reports failure instead. A command can't choose to ignore such a failure, since it's never consulted.
It would certainly be possible to implement a new shell mode that similarly skips execution and reports failure if command substitutions fail during the construction of the argument array, but this has not been a traditional feature so it's not in the POSIX spec.

Bash: how to unset an env variable with a hyphen (-)?

I have in my environment some variables that have an invalid identifier. When I try to unset them, I get the following error:
$ unset A-B
bash: unset: `A-B': not a valid identifier
How to unset them?
Related to the answer by Honza P:
According to man bash the environment variables have the form name=value where name consists only of alphanumeric characters and underscores, and begins with an alphabetic character or an underscore. So the name A-B is an invalid variable identifier for Bash, and cannot be used or accessed because it has internal validations (enforced strongly in late Bash versions because of vulnerabilities in Bash itself). The only solution is starting another secondary sub-shell that not have the offending names using the utility env to erasing them before launching Bash:
env -u 'A-B' bash
Note that the single quotes aren't needed, but to me is more readable that way as indicates the text inside is a string, not other command.
If you have more variables simply list them separated by spaces, or if you want to run only an script without the variables you can use for example:
env -u 'A-B' 'OTHER-VAR' 'bad-name' bash -c 'myscript.sh arg1 arg2'
The subshell will run myscript.sh with the arguments arg1 and arg2, and exit to the current Bash shell.
Consult the man page of 'env': man env
Not the best solution I guess but it worked in my case.
I tried an example by bishop env -u "foo-bar=baz", then env -u "foo-bar" but this approach still left some garbage in variables e.g. _=foo-bar. So I used unset _ and then it is gone.

Difference between terms: "option", "argument", and "parameter"?

What are the differences between these terms: "option", "argument", and "parameter"? In man pages these terms often seem to be used interchangeably.
A command is split into an array of strings named arguments. Argument 0 is (normally) the command name, argument 1, the first element following the command, and so on. These arguments are sometimes called positional parameters.
$ ls -la /tmp /var/tmp
arg0 = ls
arg1 = -la
arg2 = /tmp
arg3 = /var/tmp
An option is a documented1 type of argument modifying the behavior of a command, e.g. -l commonly means "long", -v verbose. -lv are two options combined in a single argument. There are also long options like --verbose (see also Using getopts to process long and short command line options). As their name suggests, options are usually optional. There are however some commands with paradoxical "mandatory options".
$ ls -la /tmp /var/tmp
option1= -l
option2= -a
A parameter is an argument that provides information to either the command or one of its options, e.g. in -o file, file is the parameter of the -o option. Unlike options, whose possible values are hard coded in programs, parameters are usually not, so the user is free to use whatever string suits his/her needs. Should you need to pass a parameter that looks like an option but shouldn't be interpreted as such, you can separate it from the beginning of the command line with a double dash: --2.
$ ls -la /tmp /var/tmp
parameter1= /tmp
parameter2= /var/tmp
$ ls -l -- -a
option1 = -l
parameter1 = -a
A shell parameter is anything that store a value in the context of the shell. This includes positional parameters (e.g. $1, $2...), variables (e.g. $foo, $bar...) and special character ones (e.g. $#)
Finally, there are subcommands, also known as functions / (low-level) commands, which are used with "metacommands" that embed multiple separate commands, like busybox, git, apt-get, openssl, and the likes. With them, you might have global options preceeding the subcommand, and subcommand specific options that follow the subcommand. Unlike parameters, the list of possible subcommands is hardcoded in the command itself. e.g.:
$ busybox ls -l
command = busybox
subcommand = ls
subcommand option1 = -l
$ git --git-dir=a.git --work-tree=b -C c status -s
command = git
command option1 = --git-dir=a.git
command option2 = --work-tree=b
command option3 = -C c
subcommand = status
subcommand option1 = -s
Note that some commands like test, tar, dd and find have more complex argument parsing syntax than the ones described previously and can have some or all of their arguments parsed as expressions, operands, keys and similar command specific components.
Note also that optional variable assignments and redirections, despite being processed by the shell for tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal like other command line parameters are not taken into account in my reply because they have disappeared when the command is actually called and passed its arguments.
1 I should have written usually documented because of course, undocumented options are still options.
2 The double dash feature need to be implemented by the program though.
The man page for a typical Unix command often uses the terms argument, option and parameter. At the lowest level, we have argument and everything is an argument, including the (filesystem path to the) command itself.
In a shell script you access arguments using the special variables $0 .. $n. Other languages have similar ways to access them (commonly through an array with a name like argv).
Arguments may be interpreted as options if you wish. How this is done is implementation-specific. You can either roll your own, for exampe a shell (such as bash) script can use provided getopts or getopt commands.
These typically define an option as an argument beginning with a hyphen (-) and some options may use proceeding arguments as its parameters. More capable parsers (e.g getopt) support mixing short-form (-h) and long-form (--help) options.
Typically, most options take zero or one parameter. Such parameters are also sometimes called values.
The supported options are coded in the program code (e.g in the invocation of getopts within a shell script). Any remaining arguments after the options have been consumed are commonly called positional parameters when the order in which they are given is significant (this is in contrast to options which usually can be given in any order).
Again, the script defines what the positional parameters are by how it consumes and uses them.
So a typical command
$ ls -I README -l foo 'bar car' baz
has seven arguments: /usr/bin/ls, -I, README, -l, foo, bar car, and baz accessible as $0 thru $6. The -l and -I are interpreted as options, the latter having a parameter (or value) of README. What remains are positional parameters (foo, bar car and baz).
Option parsing may alter the argument list by removing those it consumes (e.g using shift or set) so that only the positional parameters remain and are thereafter accessible as $1 .. $n.
Since the question is tagged "bash", I looked for relevant sections in the Bash manual. I list these as quoted passages below together with my own one sentence summaries.
Arguments
Everything following the command is an argument.
A simple shell command such as echo a b c consists of the command itself followed by arguments, separated by spaces.
A simple command is the kind of command encountered most often. It’s just a sequence of words separated by blanks, terminated by one of the shell’s control operators (see Definitions). The first word generally specifies a command to be executed, with the rest of the words being that command’s arguments.
Parameters
Arguments are referred to as parameters during function execution.
When a function is executed, the arguments to the function become the positional parameters during its execution
A parameter is an entity that stores values. It can be a name, a number, or one of the special characters listed below. A variable is a parameter denoted by a name.
A positional parameter is a parameter denoted by one or more digits, other than the single digit 0. Positional parameters are assigned from the shell’s arguments when it is invoked, and may be reassigned using the set builtin command. Positional parameter N may be referenced as ${N}, or as $N when N consists of a single digit.
Options
There is no dedicated section to defining what an option is, but they are referred to as hyphen-prefixed characters throughout the manual.
The -p option changes the output format to that specified by POSIX

Run a bash including a variable in the "bash XXX.sh" command

I am completely new to "programming" in Linux, and I wonder if it is possible to include the definition of a variable when I run a bash file.
My bash file needs the variable in order to go from one or another path, so I would like to be able to include it when running the script.
Something like this:
bash MYFILE.sh -VARIABLE
So the -VARIABLE would be used in the script.
Thank you!
You can take advantage of shell parameter expansion to smoothly read variables from the environment of the parent process, if it's that what you want to achieve.
Look at the following script named test.sh:
#!/bin/bash
VARIABLE=${VARIABLE:="default value"}
echo $VARIABLE
If you start it with the line
$ ./test.sh
it outputs
default value
But if you invoke test.sh with the line
$ VARIABLE="custom Value" ./test.sh
it outputs
custom value
But make sure that the variable assignment is at the beginning of the line. Otherwise it is passed to test.sh as command line argument.
The used form of parameter expansion ${parameter:=word} is described in the bash reference manual as:
If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional parameters and special parameters may not be assigned to in this way.

Resources