scripting

All posts tagged scripting by Linux Bash
  • Posted on

    In Bash scripting, functions are used to group a set of commands that perform a specific task. Functions can be called multiple times within a script, making your code cleaner, reusable, and easier to maintain.

    1. Defining a Function in Bash

    A function in Bash can be defined using two main syntax formats:

    Syntax 1: Using function keyword

    function function_name {
      # Commands to be executed
    }
    

    Syntax 2: Without the function keyword (more common)

    function_name() {
      # Commands to be executed
    }
    

    The second format is the more common one and is often preferred for simplicity.

    Example of Function Definition

    greet() {
      echo "Hello, $1!"  # $1 is the first argument passed to the function
    }
    

    2. Calling a Function

    Once a function is defined, you can call it by simply using its name, followed by any arguments if needed.

    Example of Function Call

    greet "Alice"
    

    Output:

    Hello, Alice!
    

    3. Passing Arguments to a Function

    Functions in Bash can accept arguments (parameters) when called. These arguments are accessed using $1, $2, etc., where $1 is the first argument, $2 is the second, and so on. $0 refers to the script's name.

    Example: Function with Arguments

    add_numbers() {
      sum=$(( $1 + $2 ))
      echo "The sum of $1 and $2 is $sum"
    }
    
    add_numbers 5 10
    

    Output:

    The sum of 5 and 10 is 15
    

    4. Returning Values from a Function

    In Bash, functions do not have a built-in return type like other programming languages (e.g., int, string). Instead, a function can return a value in two ways:

    1. Using echo or printf: You can print a value from the function, and the calling code can capture this output.
    2. Using return: This returns an exit status (0-255), which is typically used for success or failure indicators.

    Example 1: Using echo to return a value

    multiply() {
      result=$(( $1 * $2 ))
      echo $result  # Output the result
    }
    
    result=$(multiply 4 3)  # Capture the output of the function
    echo "The result is $result"
    

    Output:

    The result is 12
    

    Example 2: Using return for status (exit code)

    check_even() {
      if (( $1 % 2 == 0 )); then
        return 0  # Return 0 (success) for even numbers
      else
        return 1  # Return 1 (failure) for odd numbers
      fi
    }
    
    check_even 4
    if [ $? -eq 0 ]; then
      echo "4 is even."
    else
      echo "4 is odd."
    fi
    

    Output:

    4 is even.
    

    The special variable $? stores the exit status of the last executed command. A return value of 0 typically indicates success, while non-zero values indicate failure.

    5. Local Variables in Functions

    By default, variables inside a function are global in Bash, which means they can be accessed from anywhere in the script. To make a variable local to a function (i.e., it only exists inside that function), use the local keyword.

    Example: Local Variables

    my_function() {
      local var=10  # This variable is local to the function
      echo "Inside function: $var"
    }
    
    my_function
    echo "Outside function: $var"  # $var is not defined outside the function
    

    Output:

    Inside function: 10
    Outside function:
    

    6. Function with No Arguments

    A function can be defined and called without any arguments. The function can still perform useful tasks based on hardcoded values or other data from the script.

    Example: Function with No Arguments

    say_hello() {
      echo "Hello, World!"
    }
    
    say_hello
    

    Output:

    Hello, World!
    

    7. Returning Multiple Values from a Function

    Since Bash functions can only return one value via return (an exit status), if you need to return multiple values, the usual approach is to print the values and capture them using command substitution or use arrays.

    Example: Returning Multiple Values

    calculate() {
      sum=$(( $1 + $2 ))
      diff=$(( $1 - $2 ))
      echo "$sum $diff"  # Output both values separated by a space
    }
    
    result=$(calculate 10 5)
    sum=$(echo $result | awk '{print $1}')
    diff=$(echo $result | awk '{print $2}')
    
    echo "Sum: $sum, Difference: $diff"
    

    Output:

    Sum: 15, Difference: 5
    

    8. Default Arguments and Error Handling in Functions

    You can provide default values for arguments using Bash's conditional constructs (${1:-default_value}), and you can handle errors within functions using return or exit.

    Example: Function with Default Argument

    greet_user() {
      local name=${1:-"Guest"}  # If no argument is passed, use "Guest" as default
      echo "Hello, $name!"
    }
    
    greet_user "Alice"  # Outputs: Hello, Alice!
    greet_user          # Outputs: Hello, Guest!
    

    Example: Error Handling in Functions

    divide() {
      if [ $2 -eq 0 ]; then
        echo "Error: Division by zero!"
        return 1  # Exit with error code
      fi
      echo "Result: $(( $1 / $2 ))"
    }
    
    divide 10 2
    divide 10 0  # Error: Division by zero!
    

    Output:

    Result: 5
    Error: Division by zero!
    

    9. Function Scope

    In Bash, by default, variables are global, but functions can also define local variables using the local keyword. This ensures that variables do not conflict with those defined outside the function.

    10. Example: Putting It All Together

    Here’s an example script demonstrating all the concepts above:

    #!/bin/bash
    
    # Function to add two numbers
    add_numbers() {
      local sum=$(( $1 + $2 ))
      echo $sum
    }
    
    # Function to greet a user
    greet_user() {
      local name=${1:-"Guest"}  # Default name is Guest
      echo "Hello, $name!"
    }
    
    # Function to calculate and return multiple values
    calculate() {
      local sum=$(( $1 + $2 ))
      local diff=$(( $1 - $2 ))
      echo "$sum $diff"
    }
    
    # Calling functions
    greet_user "Alice"
    result=$(add_numbers 10 5)
    echo "The sum of 10 and 5 is: $result"
    
    # Getting multiple values from a function
    result=$(calculate 20 4)
    sum=$(echo $result | awk '{print $1}')
    diff=$(echo $result | awk '{print $2}')
    echo "Sum: $sum, Difference: $diff"
    

    Summary of Key Concepts:

    • Defining a function: function_name() { commands; }
    • Calling a function: function_name
    • Arguments: $1, $2, etc.
    • Return values: Use echo for multiple values or output, and return for exit codes (0 for success, non-zero for errors).
    • Local variables: Use the local keyword to restrict a variable to the function scope.
    • Default values: Provide default argument values using ${1:-default_value}.
    • Error handling: Use return to indicate errors within functions.

    Using functions in Bash allows you to modularize your code, improving readability and maintainability while also making it more reusable.

  • Posted on

    Loops in Bash are essential for automating repetitive tasks, iterating through lists, or executing commands multiple times. Bash provides three primary types of loops: for, while, and until. Each has its own use cases and syntax.

    1. for Loop

    The for loop in Bash is used to iterate over a list of items (such as numbers, files, or strings) and execute a block of code for each item.

    Syntax:

    for variable in list
    do
      # Commands to execute
    done
    

    Example 1: Iterating Over a List of Items

    for fruit in apple banana cherry
    do
      echo "I love $fruit"
    done
    

    Output:

    I love apple
    I love banana
    I love cherry
    

    Example 2: Iterating Over a Range of Numbers (using {})

    for i in {1..5}
    do
      echo "Number $i"
    done
    

    Output:

    Number 1
    Number 2
    Number 3
    Number 4
    Number 5
    

    Example 3: Iterating with Step Size

    You can specify a step size when iterating over a range using the seq command or a specific step in the {} range.

    for i in {1..10..2}
    do
      echo "Odd number: $i"
    done
    

    Output:

    Odd number: 1
    Odd number: 3
    Odd number: 5
    Odd number: 7
    Odd number: 9
    

    Alternatively, using seq:

    for i in $(seq 1 2 10)
    do
      echo "Odd number: $i"
    done
    

    2. while Loop

    The while loop runs as long as a given condition is true. It is useful when you don't know how many times you need to iterate, but you have a condition to check before continuing the loop.

    Syntax:

    while condition
    do
      # Commands to execute
    done
    

    Example 1: Basic while Loop

    count=1
    while [ $count -le 5 ]
    do
      echo "Count is $count"
      ((count++))  # Increment count by 1
    done
    

    Output:

    Count is 1
    Count is 2
    Count is 3
    Count is 4
    Count is 5
    

    Example 2: Looping Until a Condition is Met

    You can use a while loop to keep iterating as long as a condition is true (or until it's false).

    count=5
    while [ $count -gt 0 ]
    do
      echo "Count is $count"
      ((count--))  # Decrement count by 1
    done
    

    Output:

    Count is 5
    Count is 4
    Count is 3
    Count is 2
    Count is 1
    

    3. until Loop

    The until loop works similarly to the while loop, but it continues as long as the condition is false. It’s used when you want to execute commands until a certain condition becomes true.

    Syntax:

    until condition
    do
      # Commands to execute
    done
    

    Example 1: Basic until Loop

    count=1
    until [ $count -gt 5 ]
    do
      echo "Count is $count"
      ((count++))  # Increment count by 1
    done
    

    Output:

    Count is 1
    Count is 2
    Count is 3
    Count is 4
    Count is 5
    

    Example 2: Infinite until Loop (with a break)

    You can also create an infinite until loop. This is often used with a break statement to stop the loop when a certain condition is met.

    count=1
    until [ $count -gt 5 ]
    do
      echo "Count is $count"
      ((count++))
      if [ $count -eq 3 ]; then
        echo "Stopping at count 3"
        break
      fi
    done
    

    Output:

    Count is 1
    Count is 2
    Count is 3
    Stopping at count 3
    

    4. Loop Control Statements

    • break: Exits the loop prematurely.
    • continue: Skips the rest of the current iteration and moves to the next one.

    Example with break:

    for i in {1..5}
    do
      if [ $i -eq 3 ]; then
        echo "Breaking at $i"
        break
      fi
      echo "Number $i"
    done
    

    Output:

    Number 1
    Number 2
    Breaking at 3
    

    Example with continue:

    for i in {1..5}
    do
      if [ $i -eq 3 ]; then
        continue  # Skip the rest of the loop for i=3
      fi
      echo "Number $i"
    done
    

    Output:

    Number 1
    Number 2
    Number 4
    Number 5
    

    5. Nested Loops

    You can nest loops within each other to perform more complex tasks.

    Example: Nested for Loops

    for i in {1..3}
    do
      for j in {1..2}
      do
        echo "i=$i, j=$j"
      done
    done
    

    Output:

    i=1, j=1
    i=1, j=2
    i=2, j=1
    i=2, j=2
    i=3, j=1
    i=3, j=2
    

    Example: Nested while Loop

    i=1
    while [ $i -le 3 ]
    do
      j=1
      while [ $j -le 2 ]
      do
        echo "i=$i, j=$j"
        ((j++))
      done
      ((i++))
    done
    

    Output:

    i=1, j=1
    i=1, j=2
    i=2, j=1
    i=2, j=2
    i=3, j=1
    i=3, j=2
    

    Summary of Loops in Bash:

    1. for loop: Iterates over a list of items (or range) and executes commands for each item.

      • Best for known iterations or ranges.
    2. while loop: Executes commands as long as the condition is true.

      • Useful when you want to repeat something until a condition changes.
    3. until loop: Executes commands until the condition becomes true.

      • Opposite of the while loop; it stops when the condition is true.
    4. Loop control: Use break to exit early or continue to skip the current iteration.

    By mastering these loops and their variations, you'll be able to automate a wide range of tasks in Bash effectively!

  • Posted on

    Securing Bash scripts is essential to prevent unauthorized access, accidental errors, or malicious activity. Here are best practices to secure your Bash scripts:

    1. Use Absolute Paths

    Always use absolute paths for commands and files to avoid ambiguity and to prevent the execution of unintended commands.

    Example:

    # Incorrect
    rm -rf /tmp/*
    
    # Correct
    /bin/rm -rf /tmp/*
    

    This ensures that the correct program is used, regardless of the user's environment or $PATH settings.

    2. Avoid Using sudo or root Privileges in Scripts

    If possible, avoid running scripts with sudo or root privileges. If root access is necessary, be explicit about which commands need it, and ensure they are used sparingly.

    • Run only the necessary commands with sudo or root privileges.
    • Consider using sudo with limited privileges (using sudoers file) to allow only certain actions.

    Example (to limit permissions in sudoers file):

    user ALL=(ALL) NOPASSWD: /path/to/safe/command
    

    3. Sanitize User Input

    Validate and sanitize all user input, especially when it's passed to commands, to prevent malicious injection, such as code injection or command substitution attacks.

    Example:

    # Avoid running commands directly with user input
    read user_input
    # Vulnerable to command injection
    
    # Better approach: sanitize input
    if [[ "$user_input" =~ ^[a-zA-Z0-9_]+$ ]]; then
      # Safe to proceed with the input
      echo "Valid input: $user_input"
    else
      echo "Invalid input"
      exit 1
    fi
    

    4. Use Shellcheck for Script Linting

    Use tools like ShellCheck to lint your scripts. It helps to catch errors, warnings, and potential security issues in your code.

    shellcheck script.sh
    

    5. Set Proper File Permissions

    Set appropriate permissions for your script files to ensure they can only be executed by authorized users. You can use chmod to set permissions:

    chmod 700 /path/to/script.sh  # Only the owner can read, write, or execute
    

    6. Use set -e to Exit on Errors

    Use set -e (also known as set -o errexit) to ensure that your script exits as soon as any command fails. This can help avoid unintended behavior.

    #!/bin/bash
    set -e  # Exit on error
    

    You can also use set -u (also set -o nounset) to make your script fail if it tries to use undefined variables:

    set -u  # Treat unset variables as an error
    

    7. Quote Variables Properly

    Always quote variables to prevent word splitting or globbing issues, which can be a security risk.

    Example:

    # Vulnerable to word splitting or globbing
    file="/path/to/directory/*"
    rm $file  # This can delete unintended files
    
    # Safe way
    rm "$file"
    

    8. Log Sensitive Information Carefully

    Avoid logging sensitive information such as passwords, keys, or tokens in clear text. If necessary, ensure logs are stored securely.

    Example:

    # Don't log passwords directly
    echo "Password is: $password"  # Not secure
    
    # Instead, log securely (e.g., obfuscated or masked)
    echo "Password update successful"  # Better approach
    

    9. Limit Access to Sensitive Files

    If your script needs to access sensitive files (e.g., configuration files, private keys), make sure those files are protected with the right permissions and ownership.

    # Set permissions to restrict access to sensitive files
    chmod 600 /path/to/sensitive/file
    

    10. Avoid Hardcoding Credentials

    Never hardcode sensitive credentials such as passwords, API keys, or tokens directly in your script. Instead, use environment variables, configuration files with restricted access, or secret management systems.

    Example:

    # Avoid hardcoding secrets in the script
    api_key="your-api-key"
    
    # Better approach: Use environment variables
    export API_KEY="your-api-key"
    

    11. Use Secure Communication (TLS/SSL)

    If your script communicates over a network, always use secure protocols like HTTPS instead of HTTP. Ensure that communication is encrypted, especially when transmitting sensitive data.

    Example:

    # Vulnerable (non-secure communication)
    curl http://example.com
    
    # Secure (encrypted communication)
    curl https://example.com
    

    12. Regularly Update and Patch Dependencies

    Ensure that the tools and libraries your script depends on are kept up-to-date with the latest security patches. Regularly review the security of the script and its dependencies.

    13. Use Proper Exit Statuses

    Return appropriate exit statuses (0 for success, non-zero for failure) to indicate the result of the script’s execution. This allows better error handling and debugging.

    Example:

    #!/bin/bash
    if some_command; then
      echo "Command succeeded"
      exit 0
    else
      echo "Command failed"
      exit 1
    fi
    

    14. Use Restricted Shell (rbash) or AppArmor/SELinux

    If the script is running on a multi-user system, consider restricting the environment with tools like rbash (restricted Bash shell) or enforce security policies with AppArmor or SELinux. These tools help limit what users can do, even if they gain access to the script.

    15. Testing in a Safe Environment

    Before running a script in a production environment, test it in a controlled, isolated environment. This helps to ensure that the script works as expected without causing unintended harm.


    By following these best practices, you can significantly improve the security of your Bash scripts, minimizing the risks associated with running or sharing scripts in multi-user or production environments.

  • Posted on

    If you’ve ever used a Linux operating system used on most Virtual Private Servers, you may have heard of bash. It’s a Unix shell that reads and executes various commands.

    What Is Bash?

    Bash, short for Bourne-Again Shell, is a Unix shell and a command language interpreter. It reads shell commands and interacts with the operating system to execute them.

    Why Use Bash Scripts?

    Bash scripts can help with your workflow as they compile many lengthy commands into a single executable script file. For example, if you have multiple commands that you have to run at a specific time interval, you can compile a bash script instead of typing out the commands manually one by one. You then execute the script directly, when it’s necessary.

    Pro Tip Linux has a bash shell command manual. Type man command to find descriptions of all the technical terms and input parameters.

    Get Familiar With Bash Commands

    Bash is available on almost all types of Unix-based operating systems and doesn’t require a separate installation. You will need a Linux command prompt, also known as the Linux terminal. On Windows you would use something like PuTTy. It’s a program that contains the shell and lets you execute bash scripts. 

    1. Comments

    Comments feature a description on certain lines of your script. The terminal doesn’t parse comments during execution, so they won’t affect the output.

    There are two ways to add comments to a script. The first method is by typing # at the beginning of a single-line comment. # Command below prints a Hello World text echo “Hello, world!”

    2. Variables

    Variables are symbols that represent a character, strings of characters, or numbers. You only need to type the variable name in a command line to use the defined strings or numbers.

    To assign a variable, type the variable name and the string value like here: testvar=“This is a test variable”

    In this case, testvar is the variable name and This is a test variable is the string value. When assigning a variable, we recommend using a variable name that’s easy to remember and represents its value.

    To read the variable value in the command line, use the $ symbol before the variable name. Take a look at the example below:

    testvar=“This is a test variable”
    echo $testvar
    

    In order to let the user enter the variable contents use:

    read testvar
    echo $testvar
    

    3. Functions

    A function compiles a set of commands into a group. If you need to execute the command again, simply write the function instead of the whole set of commands.

    There are several ways of writing functions. The first way is by starting with the function name and following it with parentheses and brackets:

    function_name () {
        first command
        second command
    }
    

    Or, if you want to write it in a single line: function_name () { first command; second command; }

    4. Loops

    Loop bash commands are useful if you want to execute commands multiple times. There are three types of them you can run in bash – for, while, and until. The for loop runs the command for a list of items:

    for item in [list]
    do
        commands
    done
    

    The following example uses a for loop to print all the days of the week:

    for days in Monday Tuesday Wednesday Thursday Friday Saturday Sunday
    do
        echo “Day: $days”
    done
    

    On line 2, “days” automatically becomes a variable, with the values being the day names that follow. Then, in the echo command, we use the $ symbol to call the variable values.

    The output of that script will be as follows:

    Day: Monday
    Day: Tuesday
    Day: Wednesday
    Day: Thursday
    Day: Friday
    Day: Saturday
    Day: Sunday
    

    Notice that even with just one command line in the loop script, it prints out seven echo outputs.

    The next type of loop is while. The script will evaluate a condition. If the condition is true, it will keep executing the commands until the output no longer meets the defined condition.

    while [condition]
        do
    commands
    done
    

    5. Conditional Statements

    Many programming languages, including bash, use conditional statements like if, then, and else for decision-making. They execute commands and print out outputs depending on the conditions. The if statement is followed by a conditional expression. After that, it’s followed by then and the command to define the output of the condition. The script will execute the command if the condition expressed in the if statement is true.

    However, if you want to execute a different command if the condition is false, add an else statement to the script and follow it with the command.

    Let’s take a look at simple if, then, and else statements. Before the statement, we will include a variable so the user can input a value:

    echo “Enter a number”
    read num
    if [[$num -gt 10]]
    then
    echo “The number is greater than 10”
    else
    echo “The number is not greater than 10”
    

    OK, so that's it. The 5 building blocks of Bash in plain English. Simple, right?!