Can colon be used as identifier? - linux

I saw a code in The Bash command :(){ :|:& };: will spawn processes to kernel death. Can you explain the syntax? as follows
user#host$ :(){ :|:& };:
Here colon used as identifier for function name.
Can colon be used as identifier?

Yes, it can.
$ :()
> {
> echo "hello from : :)"
> }
$ :
hello from : :)

According to the documentation:
name
A word consisting solely of letters, numbers, and underscores, and beginning with a letter or underscore. Names are used as shell variable and function names. Also referred to as an identifier.
No, the colon is not valid in function names. So either the bomb doesn't work in bash, or the documentation is failing.
I shortly thought that the colon might refer to the built-in operator, but I don't see how that could get the expected semantics.

The implementation seems to be inconsistent. You can define and call a function containing colons and Google even suggests this for packages in their style guide.
Though I noticed you can not export nor unset these functions.
#!/usr/bin/env bash
foo:bar() {
echo "foo:bar called"
}
foo:bar
export -f foo:bar
unset foo:bar
The export won't complain but if you call another bash script afterwards foo:bar is not available.
The unset will even trigger an error:
/foo/bar: line 11: unset: `foo:bar': not a valid identifier
$ bash --version
GNU bash, version 4.2.46(1)-release (x86_64-redhat-linux-gnu)

Related

pass string with spaces to gcc [duplicate]

This question already has answers here:
How can I store a command in a variable in a shell script?
(12 answers)
Closed 4 years ago.
These work as advertised:
grep -ir 'hello world' .
grep -ir hello\ world .
These don't:
argumentString1="-ir 'hello world'"
argumentString2="-ir hello\\ world"
grep $argumentString1 .
grep $argumentString2 .
Despite 'hello world' being enclosed by quotes in the second example, grep interprets 'hello (and hello\) as one argument and world' (and world) as another, which means that, in this case, 'hello will be the search pattern and world' will be the search path.
Again, this only happens when the arguments are expanded from the argumentString variables. grep properly interprets 'hello world' (and hello\ world) as a single argument in the first example.
Can anyone explain why this is? Is there a proper way to expand a string variable that will preserve the syntax of each character such that it is correctly interpreted by shell commands?
Why
When the string is expanded, it is split into words, but it is not re-evaluated to find special characters such as quotes or dollar signs or ... This is the way the shell has 'always' behaved, since the Bourne shell back in 1978 or thereabouts.
Fix
In bash, use an array to hold the arguments:
argumentArray=(-ir 'hello world')
grep "${argumentArray[#]}" .
Or, if brave/foolhardy, use eval:
argumentString="-ir 'hello world'"
eval "grep $argumentString ."
On the other hand, discretion is often the better part of valour, and working with eval is a place where discretion is better than bravery. If you are not completely in control of the string that is eval'd (if there's any user input in the command string that has not been rigorously validated), then you are opening yourself to potentially serious problems.
Note that the sequence of expansions for Bash is described in Shell Expansions in the GNU Bash manual. Note in particular sections 3.5.3 Shell Parameter Expansion, 3.5.7 Word Splitting, and 3.5.9 Quote Removal.
When you put quote characters into variables, they just become plain literals (see http://mywiki.wooledge.org/BashFAQ/050; thanks #tripleee for pointing out this link)
Instead, try using an array to pass your arguments:
argumentString=(-ir 'hello world')
grep "${argumentString[#]}" .
In looking at this and related questions, I'm surprised that no one brought up using an explicit subshell. For bash, and other modern shells, you can execute a command line explicitly. In bash, it requires the -c option.
argumentString="-ir 'hello world'"
bash -c "grep $argumentString ."
Works exactly as original questioner desired. There are two restrictions to this technique:
You can only use single quotes within the command or argument strings.
Only exported environment variables will be available to the command
Also, this technique handles redirection and piping, and other shellisms work as well. You also can use bash internal commands as well as any other command that works at the command line, because you are essentially asking a subshell bash to interpret it directly as a command line. Here's a more complex example, a somewhat gratuitously complex ls -l variant.
cmd="prefix=`pwd` && ls | xargs -n 1 echo \'In $prefix:\'"
bash -c "$cmd"
I have built command processors both this way and with parameter arrays. Generally, this way is much easier to write and debug, and it's trivial to echo the command you are executing. OTOH, param arrays work nicely when you really do have abstract arrays of parameters, as opposed to just wanting a simple command variant.

Unix, bad substitution error?

I have the following variables :
TYP=a1
STAT_a1=statistical
FINAL_VARIABLE=${STAT_${TYP}}
But I get an error:
-bash: ${STAT_${TYP}} : bad substitution
What I want is, that the value of FINAL_VARIABLE be 'statistical'
Please help..
You can't nest variable expansions like that. But you can use indirect variable expansion with ${!varname}:
TYP=a1
STAT_a1=statistical
STAT_var=STAT_${TYP} # This sets STAT_var to "STAT_a1"
FINAL_VARIABLE=${!STAT_var} # This sets FINAL_VARIABLE to "statistical"
BTW, I recommend avoiding all-caps variable names like TYP and FINAL_VARIABLE -- there are a number of all-caps names with special meanings to the shell and/or other programs, and if you accidentally use one of those weird things can happen.
Use eval, wrapping all the stuff you want to defer evaluating in single quotes:
eval 'FINAL_VARIABLE=${STAT_'${TYP}'}'
$ TYPE=al
$ STAT_a1=statistical
$ eval 'FINAL_VARIABLE=${STAT_'${TYP}'}'
$ echo $FINAL_VARIABLE
statistical
You can also do like this
root#myagent: tmp$ F=$(echo "${STAT_a1}"_"${TYP}")
root#myagent: tmp$ echo $F
statistical_a1

escape one variable but not the other in awk

I'm facing a problem with awk in Linux. I would like to do make this script work :
awk -v var="$MYVAR" "{gsub(/export OTHER_VAR=\$OTHER_VAR:/, "var")}1" /etc/myfile
The problem here is that I want the variable "var" to be interpreted (it works) and the variable $OTHERVAR not to be interpreted, and this I what I can't manage to do.
In the end, I want to do this:
I have a variable
MYVAR=export OTHER_VAR=\$OTHER_VAR:some_text
I want to replace, in /etc/myfile, the following pattern :
export OTHER_VAR=$OTHER_VAR:/folder/bin by export OTHER_VAR=$OTHER_VAR:some_text:/folder/bin.
I hope I made myself clear ...
Thanks in advance !
Sylvain
test_document='export OTHER_VAR=$OTHER_VAR:whatever'
search_regex='^export OTHER_VAR=[$]OTHER_VAR:'
replace_str='export OTHER_VAR=$OTHER_VAR:some_text:'
awk -v search_regex="$search_regex" \
-v replace_str="$replace_str" \
'{gsub(search_regex, replace_str)} {print}' <<<"$test_document"
...properly emits as output:
export OTHER_VAR=$OTHER_VAR:some_text:whatever
Note some changes:
We're escaping the $ in the regex as [$]. Unlike \$, this is parsed consistently across all quoting contexts: It is explicitly generating a regex character class, rather than having any other potential meaning.
Using single quotes for literal strings ensures that no shell interpolation takes place within them.
Using {print} is a bit easier for readers to understand than a bare 1 in awk.
Excluding variable names with meaning to the OS or shell, use of lower-case characters in variable names is in line with POSIX-specified convention. See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html, fourth paragraph.

evaluating a string as a partial bash command

I'm writing a script in bash that reads this line from a file next-version=2.0 and I want to extract the 2.0. I can parse it as an array and split it on the =, but that feels like overkill. If I run export $VERSION which logically should become export next-version=2.0 which I could then dereference with $next-version. Instead I get bash: export: `next-version=2.0': not a valid identifier
So I tried VERSION="export $VERSION" which evaluates to export next-version=2.0 GREAT, so when I run eval $VERSION or $VERSION i get the same error. I think the next-version=2.0 is somehow maintaining the fact that it's a string? I thought it might just be upset that it's reading from lowercase, but when I repeat the process with NEXT-VERSION same result.
A function or command name in bash can contain a -, but not a parameter name. This means next-version=2.0 is not a valid assignment in bash, because the string before the = must be a valid name. The only thing you can do is parse it as you described, although there is no need for an array.
IFS== read -r name value <<< "$str"
or
value="${str#*=}" # Strip the prefix up to and including the first =
It's the -
trying $
export STRING-SS=asd
bash: export: `STRING-SS=asd': not a valid identifier
change next-version to NEXTVERSION, NEXT_VERSION, next_version or nextversion.

Bash Shell - The : Command

The colon command is a null command.
The : construct is also useful in the conditional setting of variables. For example,
: ${var:=value}
Without the :, the shell would try to evaluate $var as a command. <=???
I don't quite understand the last sentence in above statement. Can anyone give me some details?
Thank you
Try
var=badcommand
$var
you will get
bash: badcommand: command not found
Try
var=
${var:=badcommand}
and you will get the same.
The shell (e.g. bash) always tries to run the first word on each command line as a command, even after doing variable expansion.
The only exception to this is
var=value
which the shell treats specially.
The trick in the example you provide is that ${var:=value} works anywhere on a command line, e.g.
# set newvar to somevalue if it isn't already set
echo ${newvar:=somevalue}
# show that newvar has been set by the above command
echo $newvar
But we don't really even want to echo the value, so we want something better than
echo ${newvar:=somevalue}.
The : command lets us do the assignment without any other action.
I suppose what the man page writers meant was
: ${var:=value}
Can be used as a short cut instead of say
if [ -z "$var" ]; then
var=value
fi
${var} on its own executes the command stored in $var. Adding substitution parameters does not change this, so you use : to neutralize this.
Try this:
$ help :
:: :
Null command.
No effect; the command does nothing.
Exit Status:
Always succeeds.

Resources