# Built-in Functions in Bash Shell * **How to handle 'cd is a shell builtin' in Linux** https://labex.io/tutorials/linux-how-to-handle-cd-is-a-shell-builtin-in-linux-417668 ## env [options or arguments] 1. Without any argument If no flags or parameters are specified, the env command displays your current environment, showing one Name=Value pair per line. Print out a list of all environment variables. 2. ‘-i’ or ‘–ignore-environment’ or ‘only -‘ Runs a command with an empty environment ```c [ -i | - ] [Name=Value ]... [Command [ Argument ... ] ] ``` https://www.ibm.com/docs/tr/aix/7.1?topic=e-env-command --- ## pwd The default behavior of the shell built-in “pwd” is equivalent to using “pwd -L”. ```c pwd -L: Prints the symbolic path. pwd -P: Prints the actual path. ``` https://www.geeksforgeeks.org/pwd-command-in-linux-with-examples/#syntax-of-pwd-command-in-linux ### used function: getcwd ```c getcwd(): string|false ``` https://www.php.net/manual/en/function.getcwd.php --- ## echo **[options]** = The various options available for modifying the behavior of the `echo` command **[string]** = It is the string that we want to display. ```c echo [option] [string] ``` - The echo command writes character strings to standard output. - Strings are separated by spaces, and a new-line character follows the last String parameter specified. - If no String parameter is specified, a blank line (new-line character) is displayed. ```c echo -n echo -nnnnnnn ``` * **-n / -nnnnnn (followed only by character 'n'): ** -> valid option => remove \n * **-nP , -n-n, -nOPEK (followed by non 'n'):** -> invlaid --- ## exit ```c exit [N (exit_number)] ``` - The exit statement is used to exit from the shell script with a status of N. - Use the exit statement to indicate successful or unsuccessful shell script termination. - The value of N can be used by other commands or shell scripts to take their own action. - If N is omitted, the exit status is that of the last command executed. - Use the exit statement to terminate shell script upon an error. If N is set to 0 means normal shell exit. ### exit code https://hackmd.io/@QBrv51OvRPqs9dJjL2YIig/rJMw9uQf1g #### reference- check built-in exit code ```c // bi_exit: This function handles the exit command in a shell environment. // It processes the arguments provided and exits the shell with the appropriate exit code. int bi_exit(t_shell *ctx, t_arg *args) { int exit_code; // Check if there are more than one argument and if the first argument is not a valid exit code if (args && args->next && !bi_check_exitcode(args->value)) // Print error message if there are too many arguments and return with exit code 1 return (ft_putstr_fd("minishell: exit: too many arguments\n", STDERR_FILENO), 1); // Set the default exit code to 0 exit_code = 0; // Check if the first argument is a valid exit code (it should be a number) if (args && !bi_check_exitcode(args->value)) // If it's a valid exit code, convert the argument to an integer exit_code = ft_atoi(args->value); else if (args && bi_check_exitcode(args->value)) { // If the argument is invalid, call bi_err_exit to handle the error bi_err_exit(args->value); // Set exit code to 2 to indicate an error exit_code = 2; } // Close any open resources in the shell context ft_close(ctx); // Free any allocated memory in the shell context free_all_shell(ctx); // Exit the shell with the determined exit code exit(exit_code); } ////////////////////////////////// // bi_check_exitcode: This function checks if the provided value can be a valid exit code. // It ensures the value is a number and falls within the valid range of exit codes. int bi_check_exitcode(char *value) { char *tmp; long num; // Make tmp point to the provided value tmp = value; // If the value starts with '+' or '-', skip the sign if ((*tmp == '+' || *tmp == '-') && *(tmp + 1)) tmp++; // Iterate through each character in the value to check if it's a digit while (*tmp) { // If any character is not a digit, return 1 indicating an invalid exit code if (!ft_isdigit(*tmp++)) return (1); } // Convert the value to a long integer using ft_atol num = ft_atol(value); // Check if the number overflows or underflows the range of valid exit codes if ((num > 0 && (LONG_MAX / num < 1)) // Overflow check for positive numbers || (num < 0 && (LONG_MIN / ft_atol(value) < 1))) // Underflow check for negative numbers return (1); // Return 1 to indicate an invalid exit code // Return 0 if the value is a valid exit code (no overflow or non-digit characters) return (0); } ``` #### **1. Why would `value` start with `+` or `-`?** In many programming environments (including shells), exit codes (or return codes) are often numeric values that can be either positive or negative. For example: - A return code of `0` typically indicates successful execution. - Non-zero return codes (positive or negative) usually indicate some form of error or abnormal behavior. In C and many other languages, numeric values can optionally be prefixed with a sign (`+` or `-`) to indicate whether they are positive or negative: - If the value starts with a `+`, it means the number is positive (e.g., `+42` is just `42`). - If the value starts with a `-`, it means the number is negative (e.g., `-1` represents negative one). **Why check for these signs?** - The function allows for both positive and negative exit codes. - The logic is implemented to handle the possibility that a user might input a string like `"42"` or `"-42"` or `"+42"`. For example: ```c bi_check_exitcode("-42"); // Valid, negative number bi_check_exitcode("+42"); // Valid, positive number ``` #### **2. Why use `ft_atol`?** The function uses `ft_atol` (presumably a function that converts a string to a long integer) to convert the input `value` (which is a string) into a numerical form. Here's why this is necessary: - **Exit Code Representation**: The exit code is a number, but the user may input it as a string (e.g., `"42"`, `"-1"`, etc.). - **Numeric Validation**: After checking that the string consists of digits (and possibly a sign), `ft_atol` is used to convert the string into a long integer (`long`). This allows for more meaningful validation, such as checking for overflow/underflow, and handling large or negative numbers that are typically seen in shell exit codes. `ft_atol` is used instead of `atoi` to allow handling larger numbers because `long` provides a larger range than `int`. Example: ```c // If user inputs "-123456789", we convert it to a long long exit_code = ft_atol("-123456789"); // This converts the string into the number -123456789 ``` #### **3. Explanation of Overflow and Underflow Checks** The overflow and underflow checks ensure that the exit code falls within the valid range for an exit code, which typically must be a number between `0` and `255` (in many shell environments). Since `LONG_MAX` and `LONG_MIN` are much larger than this range, we need to ensure that the value is within the bounds. Here are the checks and why they are necessary: ```c if ((num > 0 && (LONG_MAX / num < 1)) // Overflow check for positive numbers || (num < 0 && (LONG_MIN / ft_atol(value) < 1))) // Underflow check for negative numbers ``` #### **Overflow Check for Positive Numbers (`num > 0 && (LONG_MAX / num < 1)`)** - `LONG_MAX` is the largest value that can fit into a `long` variable. On most systems, `LONG_MAX` is `9223372036854775807` (for a 64-bit system). - If the `num` is positive, we check if multiplying it by `1` would cause it to exceed `LONG_MAX`. - For example, if `num = 1000000000`, `LONG_MAX / num` will give a number greater than `1`. But if `num = 9223372036854775808`, `LONG_MAX / num` will be less than `1`, indicating that `num` is larger than `LONG_MAX`. - **Overflow means a number exceeds the maximum value that can be represented**, causing it to wrap around and produce unpredictable results. #### **Underflow Check for Negative Numbers (`num < 0 && (LONG_MIN / ft_atol(value) < 1)`)** - `LONG_MIN` is the smallest value that can fit into a `long` variable, and it’s usually `-9223372036854775808` for a 64-bit system. - If `num` is negative, we check if dividing `LONG_MIN` by `num` gives a result less than `1`. This indicates that `num` is too small (i.e., it’s below the valid range of exit codes). - For example, if `num = -9223372036854775809`, `LONG_MIN / num` would be less than `1`, signaling an underflow (the number is too small to be a valid exit code). - **Underflow means the number is too small, causing issues when handling negative numbers in certain systems**. #### **Examples** - **Overflow Example**: ```c num = 9223372036854775808; // Exceeds LONG_MAX if (LONG_MAX / num < 1) { // This condition will be true, indicating overflow return 1; // Invalid exit code } ``` - **Underflow Example**: ```c num = -9223372036854775809; // Less than LONG_MIN if (LONG_MIN / num < 1) { // This condition will be true, indicating underflow return 1; // Invalid exit code } ``` In summary: - **Overflow** occurs when a number is too large to be represented in the target type (in this case, `long`). - **Underflow** occurs when a number is too small, and it is below the minimum representable value (i.e., smaller than `LONG_MIN`). - These checks prevent invalid or incorrectly interpreted exit codes, ensuring that the exit code remains within a valid and manageable range. The checks `(LONG_MAX / num < 1)` and `(LONG_MIN / num < 1)` are designed to detect overflow and underflow situations when handling large or small numbers. Let’s break down the logic behind each of these checks and why division is used. #### **Explaination on why divide to overflow/undrflow?** - When you divide by `num`, you are essentially checking if multiplying `num` by any value (like `1`) will result in a number that exceeds the maximum or minimum limit that can be represented by the `long` type. - This is important because `LONG_MAX` and `LONG_MIN` represent the maximum and minimum values that a `long` can hold. If you try to assign a value larger than `LONG_MAX` or smaller than `LONG_MIN` to a variable of type `long`, it will overflow or underflow, respectively. - To **check for overflow** or **underflow** without actually performing the multiplication (which could cause the overflow or underflow), the division check ensures that multiplying by `num` would exceed the boundaries of the `long` type. #### **Overflow Check: `(LONG_MAX / num < 1)`** - `LONG_MAX` is the largest value that can be stored in a `long` type. - If we divide `LONG_MAX` by `num`, we want to check if multiplying `num` by any value, especially `1`, will cause the value to exceed `LONG_MAX`. ##### Example: Let's say `num = 9223372036854775808` (a number that exceeds `LONG_MAX` for 64-bit systems). - On a 64-bit system, `LONG_MAX` is typically `9223372036854775807`. - Now, if we divide `LONG_MAX` by `num`: ```c long num = 9223372036854775808; // A value that exceeds LONG_MAX if (LONG_MAX / num < 1) { // This will evaluate to true because the result of the division // is smaller than 1, indicating that num is too large and would overflow } ``` - Dividing `LONG_MAX` by `9223372036854775808` gives a result smaller than `1`, which means multiplying `num` by any number (especially `1`) would exceed `LONG_MAX`. This indicates an overflow would occur if you tried to store `num` in a `long`. #### **Underflow Check: `(LONG_MIN / num < 1)`** - `LONG_MIN` is the smallest value that can be stored in a `long` type. - Similar to the overflow check, this division checks if dividing `LONG_MIN` by `num` would lead to a result smaller than `1`. If it does, it means that multiplying `num` by any value would cause underflow (i.e., the result would go below the minimum representable value for a `long`). ##### Example: Let’s take `num = -9223372036854775809`, which is a number smaller than `LONG_MIN` for a 64-bit system. - On a 64-bit system, `LONG_MIN` is typically `-9223372036854775808`. - Dividing `LONG_MIN` by `num`: ```c long num = -9223372036854775809; // A value that is smaller than LONG_MIN if (LONG_MIN / num < 1) { // This will evaluate to true because dividing LONG_MIN by num // will result in a number smaller than 1, indicating an underflow } ``` - Dividing `LONG_MIN` by `-9223372036854775809` results in a value smaller than `1`, which means multiplying `num` by any number (especially `1`) would cause underflow. This indicates that `num` is too small to be represented in a `long`. #### **Summary:** - **Overflow Check**: `(LONG_MAX / num < 1)` is checking if multiplying `num` by any value would exceed the maximum value a `long` can hold. If dividing `LONG_MAX` by `num` results in a value smaller than `1`, it means multiplying `num` by `1` would exceed `LONG_MAX`, causing an overflow. - **Underflow Check**: `(LONG_MIN / num < 1)` is checking if multiplying `num` by any value would cause the result to go below the minimum value a `long` can hold. If dividing `LONG_MIN` by `num` results in a value smaller than `1`, it means multiplying `num` by `1` would cause an underflow. -> These checks ensure that `num` does not exceed the boundaries of the `long` data type, thus preventing overflow and underflow. -> Division is used here because it allows us to avoid performing the potentially dangerous multiplication that would result in an actual overflow or underflow. --- - **Exit command:** https://bash.cyberciti.biz/guide/Exit_command#Syntax - **Bash Exit Command Explained: Script Terminating Tutorial:** https://ioflood.com/blog/bash-exit/ - **How to use exit code to read from terminal, from script and with logical operators:** https://www.geeksforgeeks.org/how-to-use-exit-code-to-read-from-terminal-from-script-and-with-logical-operators/ --- ## cd ```cl cd [options] [directory] ``` * Cd Command in Linux (Change Directory) https://linuxize.com/post/linux-cd-command/ * How to Change the Directory in Linux | cd Command https://www.geeksforgeeks.org/cd-command-in-linux-with-examples/ ##### cd with [options] ```c cd cd -- (In the POSIX standard, cd -- behaves like running cd without arguments.) cd - () cd .. cd / cd ~ ``` ```c int chdir(const char *path); ``` chdir() changes the current working directory of the calling process to the directory specified in path. --- # export ```c export (To view all the exported variables.) export -p (To view all exported variables on current shell. ) export -f [function_name] (To export shell function) ***Must be used if the names refer to functions. *** If -f is not used, the export will assume the names are variables. export -n [variable_name] (Named variables (or functions, with -f) will no longer be exported.) ***No output will be seen on screen, to see exported variable grep from exported ones is used. ``` --- ## **POSIX Behavior of the `export` Command** The `export` built-in command in **bash**: 1. **Without Arguments**: - The environment variables are displayed in **alphabetical order**. - Each variable is printed in the format: ``` declare -x VAR="value" ``` - Sorting the environment ensures consistent behavior when users call `export`. 2. **With Arguments**: - If you pass arguments like `export VAR=value`, the variable is added or updated in the environment list **without requiring sorting** immediately. --- ## **When Sorting enviromet variable is Needed?** - **If the `export` command is called without arguments**, you must print the environment variables **in alphabetical order**. Example: ```bash export ``` Output: ``` declare -x HOME="/home/user" declare -x PATH="/usr/bin" declare -x SHELL="/bin/bash" ``` - Sorting ensures consistent behavior regardless of the order in which variables were added. --- ## **Optimizing Sorting** If sorting becomes a bottleneck, you can **optimize** it: 1. **Sort Only When Printing**: - Instead of sorting the entire environment list every time you modify it, sort only when you need to print the variables (e.g., inside `ft_export_noarg`). Example: ```c void ft_export_noarg(char **env) { ft_advanced_sort_string_tab(env, &ft_strcmp); // Print the sorted environment for (int i = 0; env[i]; i++) printf("declare -x %s\n", env[i]); } ``` 2. **Avoid Duplicate Sorting**: - If the environment is already sorted and hasn’t been modified, there’s no need to sort it again. You can add a **"sorted" flag** to track whether sorting is needed. --- ## **Conclusion** - **Yes**, sorintg envionmet variables is necessary when the `export` command is called **without arguments** to ensure the environment variables are displayed in **alphabetical order**, as expected in a POSIX-compliant shell. - If you only add or update variables with `export VAR=value`, sorting is not required at that stage. - For better performance, sort the environment **only when printing** and avoid redundant sorting by tracking changes to the environment list. --- # unset ```c unset [variable_name] unset [-options] [variable_name] (Undefine a variable in bash) unset -f [function_name] (Undefine shell function in bash) ``` * **Unset Syntax** https://bash.cyberciti.biz/guide/Unset * **Bash remove environment variables command** https://www.cyberciti.biz/faq/bash-remove-environment-variables-command/ * Linux unset command summary with examples https://www.youtube.com/watch?v=yDPRQ28dV6E&list=PL6YwPExkSESqQ69B7B011XOIuoVv3SdDg&index=50