Bash unbound variable array (script: s3-bash) - bash

I am working with: s3-bash, when I run it in my local environment (OS X 10.10.1) I don't have any problems, when I try to run it on a ubuntu server 14.04.1 I get the following error:
./s3-common-functions: line 66: temporaryFiles: unbound variable
./s3-common-functions: line 85: temporaryFiles: unbound variable
I've looked at the s3-common-functions script and the variable looks to be initialized properly (as an array):
# Globals
declare -a temporaryFiles
But there is a note in the comment, and I'm sure if it's related:
# Do not use this from directly. Due to a bug in bash, array assignments do not work when the function is used with command substitution
function createTemporaryFile
{
local temporaryFile="$(mktemp "$temporaryDirectory/$$.$1.XXXXXXXX")" || printErrorHelpAndExit "Environment Error: Could not create a temporary file. Please check you /tmp folder permissions allow files and folders to be created and disc space." $invalidEnvironmentExitCode
local length="${#temporaryFiles[#]}"
temporaryFiles[$length]="$temporaryFile"
}

There appears to be a bash behaviour change at play here.
Found by kojiro: CHANGES
hhhh. Fixed a bug that caused `declare' and `test' to find variables that
had been given attributes but not assigned values. Such variables are
not set.
$ bash --version
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
$ set -u
$ declare -a tF
$ echo "${#tF[#]}"
0
vs.
$ bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ set -u
$ declare -a tF
$ echo "${#tF[#]}"
0
vs.
$ bash --version
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ set -u
$ declare -a tF
$ echo "${#tF[#]}"
-bash: tF: unbound variable
You can use declare -a tF=() on the newer bash versions to work around this.
$ declare -a tF=()
$ echo "${#tF[#]}"
0

Bash can substitute an empty value into unset variables using a dash.
set -u
my_array=()
printf "${my_array[#]-}\n"
This specific example will print nothing, but it will not give you an unbound variable error either.
Stolen from Bash: Error `Unbound variable' when appending to empty array1.
However, when used for a loop, you get one iteration (not zero) then:
for i in "${my_array[#]-}"
do
echo $i
done
Expectation might be to get zero iterations.
It could be fixed this way then:
[ ${#my_array[#]} -gt 0 ] &&
for i in "${my_array[#]}"
do
echo $i
done

Changed declaration of array for temporaryfiles
declare -a temporaryFiles
to:
temporaryFiles=()
Why this is different / non-function in ubuntu 14.04.1 Linux 3.13.0-32-generic x86_64 versus OS X I am not sure ?

find $fullfolder -type f |
while read fullfile
do
filename=$(basename "$fullfile")
ext=$([[ $filename = *.* ]] && printf %s ${filename##*.} || printf 'NONE')
arr+=($ext)
echo ${#arr[#]}
done
echo ${#arr[#]}
Why does the ${#arr[#]} inside the for loop produce the correct result but the one outside gives 0?

Related

How to separate outputs of one-line multiple commands?

I have installed some GNU packages on my macOS Sierra, which include bash, coreutils, which, etc. Now I can use which -a bash | xargs -I % echo % "--version" | sh to check all version info of bash, but there is no separation between two version info:
$ which -a bash | xargs -I % echo % "--version" | sh
GNU bash, version 4.4.12(1)-release (x86_64-apple-darwin16.3.0)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
# There should be one or more blank lines as separation.
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
Copyright (C) 2007 Free Software Foundation, Inc.
I tried ... echo -e % "--version\n" ..., but it can't work. Please help me, thanks.
For a bit more control of the output, use a loop:
which -a bash |
while read cmd; do
printf '%s\t----------\n' "$cmd"
command "$cmd" --version
echo
done
I'd write this as follows:
IFS=: read -r -a path_entries <<<"$PATH"
find "${path_entries[#]}" -maxdepth 1 -name bash -type f -exec '{}' --version ';'
Note:
No use of which. This isn't a shell builtin in bash, so it gives you none of the benefits you'd get from type (ie. awareness of functions and aliases); and because its output is line-oriented, it can't unambiguously emit all possible filenames (such as those containing newlines -- which, yes, are valid).
No reliance on line-oriented content, generally. When the placeholder ({}) is passed as a single token, find -exec substitutes the exact filename returned for that token. Because the domain of possible argv values (all valid C strings, which is to say, no strings containing NULs) matches the domain of possible filenames on common systems, this avoids introducing potential bugs.
No generating text and piping that to an interpreter. This is an extremely error-prone technique, inducing shell-injection vulnerabilities: Consider what would happen if a PATH contained /tmp/$(rm -rf ~), and that directory contained a bash executable.

Non-integer counter for do ... until loop in bash

I'm currently using bash 3.2.51
I'm trying to create a simple do until loop that steps up in small intervals of 0.05. My current code is shown below, it works fine for whole numbers but when I use non-integer it fails with errors.
x=0.05
until [ $x -gt 1.20 ]
do
//some code //
x =$((x+0.05))
done
Any help would be much appreciated.
bash currently doesnt support float variables, you need to delegate the task to other programs.
$echo $((1+0.05))
bash: 1+0.05: syntax error: invalid arithmetic operator (error token is ".05")
$ echo $(echo "1+0.05" | bc -l)
1.05
$ bash -version
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$
more info: https://unix.stackexchange.com/questions/40786/how-to-do-integer-float-calculations-in-bash-or-other-languages-frameworks

How to properly escape string to test in shell?

I have the following function:
testit()
{
[ "$1" == "OK" ] && echo "good" || echo "bad"
}
but it doesn't work as expected. I can call it with:
testit 'OK' #good
testit 'abc' #bad
but when I call it with:
testit '('
it fails with: "sh: closing paren expected". I've quoted the string to test, so why it acts like there are no quotes?
This is most likely a bug in your shell implementation. I have tested your code on the latest CentOS and it works fine.
testit '('
bad
sh -version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Here's an ugly workaround:
if [ "x$1" = "xOK" ]; ...

Bash Array Feature or Bug?

While attempting to debug a job submission script, I ended up narrowing down the bug to this:
[testuser#bes ~]$ var=( 1 foo1*bar4 echo 1*4=4 )
[testuser#bes ~]$ echo "${var[#]}"
1 foo1*bar4 echo 1*4=4
[testuser#bes ~]$ cd /data/testuser/jobs/example/a16162/
[testuser#bes a16162]$ var=( 1 foo1*bar4 echo 1*4=4 )
[testuser#bes a16162]$ echo "${var[#]}"
1 foo1-bar4 foo1*bar4 echo 1*4=4
[testuser#bes a16162]$
That is an uncut transcript of a fresh bash session. Anyone have any idea how that one works? Is this some archaic feature of bash that I've never heard of before, or just a really weird bug?
Versions (yes I know it's out dated):
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
Linux bes 2.6.18-194.11.3.el5 #1 SMP Mon Aug 30 16:19:16 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux
EDIT: This is for something that needs to process a user-passed array, and I'd rather use this method than a triplet of rather awkward awk hacks. They're trivial "extract element 2" sorts of things, which is why using the array seems nicer.
Globs are still globbed when the array is formed. If you don't want this then you need to quote or escape them.
$ var=( 1 "foo1*bar4" echo "1*4=4" )
What does ls /data/testuser/jobs/example/a16162/foo1* reveal?
You can disable filename globbing with set -f and re-enable it with set +f

Unix commandline history substitution ^foo^bar (for multiple replacements)

Occasionally I use the bash command to replace string in previous command:
^foo^bar
Today I wanted to make the replacement in the following line for replacing all occurrences of checkbox with `radio:
$ git mv _product_checkbox_buttons.html.erb _product_checkbox_button.html.erb
$ ^checkbox^radio
git mv _product_radio_buttons.html.erb _product_checkbox_button.html.erb
So it only replaces the first occurrence. How do I make it replace all occurrences?
bash --version
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin10.0)
Copyright (C) 2007 Free Software Foundation, Inc.
man bash says ^old^new is equivalent to !!:s/old/new/. You want !!:gs/old/new/ to globally replace.
An easy way of doing this would be:
fc -s checkbox=radio
I have an alias r defined in my .bashrc:
alias r='fc -s'
Then what you want to do becomes:
r checkbox=radio
See my answer to "hidden features of bash" question for details.

Resources