[Bash] Use of brackets in this code snippet

  • #1
Wrichik Basu
Insights Author
Gold Member
2020 Award
1,710
1,556
Disclaimer: I am an absolute beginner in bash scripting.

I spent the whole evening trying to make this code snippet work as intended. The program is supposed to sort an array using bubble sort algorithm.
Program:
#!/bin/bash

arr=(7 -1 9 2 11 -13 5)

for (( i=0; i<(( ${#arr[@]} - 1 )); i++ )) ; do
    for (( j=0; j<(( ${#arr[@]} - 1 - i )); j++ )); do
        if (( arr[j] > arr[j+1] )); then
            temp=${arr[j]}
            arr[j]=${arr[j+1]}
            arr[j+1]=${temp}
        fi
    done
done

echo -ne "\n"
printf '%s\n' "${arr[@]}"
Output:
-13
-1
2
5
7
9
11
I knew the algorithm well; the only problem I faced was brackets. Curly brackets, parenthesis, square brackets — all got mixed up. Later, I found this question on Stack Overflow that outlines the different brackets and their meanings. That solves most of the queries, but some remain:
  1. Square brackets, [[...]] are used for logical expressions. Then why does neither if [[ arr[j] > arr[j+1] ]] nor if [[ ${arr[j]} > ${arr[j+1]} ]] work? (On the other hand, both if (( ${arr[j]} > ${arr[j+1]} )) and if (( arr[j] > arr[j+1] )) works.)
  2. I found that I get the correct output even if I don't use $ in front of i and j. If bash can substitute the value of the variable here, why can't it do the same in other places? (For example, in lines 8, 9 and 10, I have to use ${...} on the RHS of the = operator.)
 

Answers and Replies

  • #3
12,755
6,627
Most bash scripters when faced with what you’re doing will quickly shift to python to get it done.

Bash is great for simple scripting and extremely dense for more complex stuff.
 
  • Like
Likes pbuk, FactChecker and PeterDonis
  • #4
Wrichik Basu
Insights Author
Gold Member
2020 Award
1,710
1,556
Most bash scripters when faced with what you’re doing will quickly shift to python to get it done.
I am doing it because I have to do it for my computer science elective this semester. Given a choice, I would have certainly chosen Java in most cases.
 
  • #5
12,755
6,627
I understand, I was just giving you my experience. I’ve tried to extend some scripts ans the peculiarities of bash always get in the way usually around single quotes vs double quotes.
 
  • Like
Likes Wrichik Basu
  • #6
Ibix
Science Advisor
Insights Author
2020 Award
7,661
6,846
Regarding OP's question 2 - I believe that you don't need the $ signs in front of variables when they are used in mathematical expressions. Presumably array indices count as mathematical expressions, hence ${arr[i]} not needing a dollar before the i.

I don't think it's a case of bash not being able to substitute a variable value, but that you don't want it to substitute a value unless you explicitly tell it to (or use the variable in a mathematical context where it's clear it should be used as a variable). Remember that this is a shell script - it's largely used to run operating system commands. So say you define a variable called ls. What do you want bash to do with the characters ls - interpret them as a variable and fetch the value, or execute the ls command? You could make known Unix commands reserved words, but you can't write an exhaustive list of programs you could be running so you would always risk a collision. Bash solves the problem allowing variable names to be (almost) anything and requiring you to write $ls to get the value of the variable.
 
  • Informative
Likes Wrichik Basu
  • #7
12,755
6,627
The single quote double quote issue is that single quotes are for literals ie no substitutions

echo "The book title is: $1" (prints the first script arg remember $0 is the script name itself)

vs

echo 'The book cost: $1' (prints the literal 'the book cost $1')

For some commands you want the delayed expansion of a file mask as in the command

find . -name '*.jpg' (to find all jpg files with the current directory tree)
 
  • Like
  • Informative
Likes Wrichik Basu and Ibix
  • #8
12,755
6,627
The best script development strategy is to use echo statements to see what happens and to add your changes slowly and carefully, testing in between to make sure things are working as you expect.

I think bash also has a no-execute flag where the script will display with substitutions and adjustments but not execute and commands.

as in bash -n for noexecute:

https://tldp.org/LDP/abs/html/options.html

Table 33-1. Bash options

Abbreviation​
Name​
Effect​
-B​
brace expansion​
Enable brace expansion (default setting = on)​
+B​
brace expansion​
Disable brace expansion​
-C​
noclobber​
Prevent overwriting of files by redirection (may be overridden by >|)​
-D​
(none)​
List double-quoted strings prefixed by $, but do not execute commands in script​
-a​
allexport​
Export all defined variables​
-b​
notify​
Notify when jobs running in background terminate (not of much use in a script)​
-c ...​
(none)​
Read commands from ...
checkjobs​
Informs user of any open jobs upon shell exit. Introduced in version 4 of Bash, and still "experimental." Usage: shopt -s checkjobs (Caution: may hang!)​
-e​
errexit​
Abort script at first error, when a command exits with non-zero status (except in until or while loops, if-tests, list constructs)​
-f​
noglob​
Filename expansion (globbing) disabled​
globstar​
Enables the ** globbing operator (version 4+ of Bash). Usage: shopt -s globstar​
-i​
interactive​
Script runs in interactive mode​
-n
noexec
Read commands in script, but do not execute them (syntax check)
-o Option-Name​
(none)​
Invoke the Option-Name option​
-o posix​
POSIX​
Change the behavior of Bash, or invoked script, to conform to POSIX standard.​
-o pipefail​
pipe failure​
Causes a pipeline to return the exit status of the last command in the pipe that returned a non-zero return value.​
-p​
privileged​
Script runs as "suid" (caution!)​
-r​
restricted​
Script runs in restricted mode (see Chapter 22).​
-s​
stdin​
Read commands from stdin​
-t​
(none)​
Exit after first command​
-u​
nounset​
Attempt to use undefined variable outputs error message, and forces an exit​
-v​
verbose​
Print each command to stdout before executing it​
-x​
xtrace​
Similar to -v, but expands commands​
-​
(none)​
End of options flag. All other arguments are positional parameters.​
--​
(none)​
Unset positional parameters. If arguments given (-- arg1 arg2), positional parameters set to arguments.​
 
  • Like
Likes Wrichik Basu
  • #9
Wrichik Basu
Insights Author
Gold Member
2020 Award
1,710
1,556
Thanks, @Ibix; that also explains why ${arr[i+1]} or similar expressions execute without the need of double parenthesis inside the square brackets.
The single quote double quote issue is that single quotes are for literals ie no substitutions
I haven't used parameterized functions yet, but was hoping to use them soon. Thanks for pointing out this subtlety; otherwise I would have had to spend hours not realizing where the error is occurring.

Also, I have made one error in the program: > or < cannot be used for numeric comparisons; they are for strings. Instead, I have to use -lt or -gt and so on.

An interesting note regarding question no. 1: the following works: if [[ ( ${arr[j]} -gt ${arr[j+1]} ) ]];, which probably means I have to evaluate the expression inside a subshell to make it work.
 
  • #10
12,755
6,627
I think the < and > re used for input output redirections:

echo "hello world" >somefile.txt

cat <somefile.txt

cat somefile.txt
 
  • Like
Likes Wrichik Basu

Related Threads on [Bash] Use of brackets in this code snippet

Replies
1
Views
2K
  • Last Post
Replies
1
Views
8K
  • Last Post
Replies
10
Views
12K
Replies
6
Views
530
  • Last Post
Replies
9
Views
5K
Replies
5
Views
5K
Replies
9
Views
1K
  • Last Post
Replies
2
Views
6K
Replies
4
Views
2K
  • Last Post
Replies
2
Views
33K
Top