Why do many programmers avoid using global variables and are there any examples of security breaches caused by using global variables?
In tiny projects global variables are just fine. Most of their problems don't show up. And the above Bash environment customization is indeed Tiny.
Global variables start causing problems as your program scales; which can happen in a bunch of ways, but the core part of the problem is the amount of state that understanding each piece of code requires.
Imagine a 10 million line code base. In it, there are 1 million variables. All of them are global.
In a given 100 line piece of code, you might use 20 variables.
Whenever you call a function, you have no idea which of those 20 variables that function will modify; when your function starts, you have no idea where the variable state came from.
So to understand what your 100 lines of code mean, you need to either understand and hold in your head all 10 million lines of code, or you need some kind of convention about where data is coming from, what data can and cannot be modified when you call a helper function, etc.
In contrast, if your code is almost entirely fueled by function arguments, local variables and return types, you only have to understand your function to understand what it does.
Moreover, if that function calls another function, you know what information you are passing to it, and what information it passes back.
You might not know what it does with that information, but you know what it doesn't do.
For example, it cannot modify
integer x and you are certain, because you didn't pass
x to the function, nor did you assign to it from its return value!
Making code easier to reason about locally is a very important goal. You want to be able to read a function, know what it is doing, and identify bugs.
Writing code is far far easier and far far less important than making code that is clear and easy to reason about.
Almost all code in a large, persistent project gets read 100s of times more often than it is modified, and it is written and designed once.
From this rule -- make stuff easier to reason about locally -- we reach the rule of "avoid global state".
Global variable versus Super-object
Global variables that are read/written are an example of global state. But so can be some super-object that everything in your project has a pointer to passed as the first argument.
A super-object still has advantages over global variables; global variables often have confusing mechanics on how they are initialized and cleaned up (I'm looking at you, C/C++), and if you have a super-object with all of your "global" state you can instantiate two versions of your super-object and run both at the same time in the same process (this will tend not to work, but that is usually because of some implicit global state the OS foists on you).
Every global variable you read, instead of a parameter, means that any bit of code, anywhere could be modifying its behavior. Every global variable you write to, means you are changing the behavior of some code arbitrarily far away.
And it isn't just humans who find global states a pain; if your state is local, writing test harnesses that "fake" a global state or environment becomes far easier. If there is (say) a global "mouse" object, writing a unit test that creates a fake mouse that pretends to do certain movement becomes harder it may even be harder to write a macro that plays back a recorded mouse movement into some code, especially if you intend to do it while the UI remains responsive to the actual human using a mouse.
Security is a function of understanding what your code does. Security, within a program, means "your code only does what it is intended to be allowed to do"; with global variables, you have a weaker ability to understand what the code does, thus less ability to control what it does.