Use these coding tips to write fast, readable shell scripts.
Bash has undoubtedly become the native built-in automation solution for every modern Unix-like system or Unix-based operating system. Programmers use Bash to create shell scripts to automate repetitive command-line tasks. The main goal of Bash is to provide a minimal syntax for executing other programs and handling their exit codes and output. However, modern Bash interpreters have a complete command language that provides the functionality of most general-purpose programming languages. Therefore, we can write highly readable shell scripts by including traditional command line calls and algorithmic code. Modern versions of Bash introduced performance-related features such as associative arrays and support for pass-by-reference, giving Bash the ability to compete with other languages ready for shell scripting.
In this article, I will introduce some Bash coding tips that you can include in your shell scripts to make them modern, fast and readable. Using these tips, you can use Bash to write general programming or algorithm implementations, such as algorithm prototypes, implementation utilities, or even competitive programming!
Traditional Bash variables usually have no type, but you can process them as integers, decimals, or strings depending on the specific processing context. We usually use Bash variables to store command output, algorithm parameters, and other temporary values. Bash also supports two array types: one-dimensional (numeric index) and associative (key-value structure). As with other popular dynamically typed general-purpose languages such as Python, PHP, or JavaScript, working with Bash arrays is very easy. Here's how to create an array in Bash:
#!/bin/bash #Linux迷 www.linuxmi.com numbers=(9 3 1 3 9) declare -a words words[0]='Linux迷' words[1]='LinuxMi' echo ${numbers[@]} ${words[@]}
The array content output by the above code is as follows:
You can check the declaration of each array reference via the declare built-in command as follows:
Checking array declarations in Bash, author’s screenshot You can also use minimal syntax for array operations and processing activities such as appending new items, deleting existing items, processing array elements, sorting, etc. For example, the following code removes invalid score values and prints the top three highest scores:
#!/bin/bash #Linux迷 www.linuxmi.com declare -a marks marks+=(75 65 80 102 26) # class A marks marks+=(103 68) # class B marks # 删除无效标记 for i in "${!marks[@]}"; do if ((marks[i] > 100)); then unset "marks[$i]" fi done # 对所有标记进行排序 marks_s=($(printf '%s\n' "${marks[@]}" | sort -nr)) # 打印前 3 名 echo ${marks_s[0]} ${marks_s[1]} ${marks_s[2]}
The above code will spawn a separate process for sorting because we use the sort external command, but you can avoid this by using some Bash code to implement a simple sorting algorithm, such as selection sort.
In some programming scenarios, we need to store key-value pair data in shell scripts. Programmers often use key-value data structures to create dictionary structures, mappings, and cache containers (via memoization). If you write your shell scripts in Python, you can use the built-in dictionary data structure to store key-value data. How to use dictionary structure in Bash?
Bash version 4.0 introduced the associative array function for storing key-value data. Here is a simple example of a Bash associative array:
#!/bin/bash #Linux迷 www.linuxmi.com declare -A marks=([linux]=39 [ubuntu]=27 [debian]=83 [fedora]=59) for key in "${!marks[@]}"; do printf "$key \t ${marks[$key]} \n" done
Here, we have used the !mapvar[@] syntax to extract all dictionary keys as an array to iterate over. The above code will print out all keys and corresponding values as follows:
Bash allows you to manipulate and access associative array data using minimal syntax. Using Bash associative arrays works just like using Python dictionaries. Please see the example below:
#!/bin/bash #Linux迷 www.linuxmi.com read -p "Enter coords (i.e., [x]=10 [y]=12): " coords declare -A "coords=($coords)" if [ ! -v "coords[x]" ]; then coords[x]=5 fi if [ ! -v "coords[y]" ]; then coords[y]=10 fi for key in "${!coords[@]}"; do printf "$key = ${coords[$key]} \n" done
The above source code requests x and y coordinates from the user, sets default values for missing axis values, and prints them on the terminal. Here, we use ! -v syntax because usually we use not in in Python dictionary.
当您通过 Bash 解释器执行 shell 脚本时,操作系统会创建一个新的 Bash 进程,并将您的脚本文件作为第一个命令行参数。操作系统通常允许您将一系列参数传递给每个操作系统进程。当您为其他命令/进程提供命令行参数时,您也可以将它们传递到您的 Bash 脚本中。假设您需要将两个整数值传递给脚本。然后,您可以轻松使用 和2 分别访问第一个和第二个参数值。但是,当您使用更多索引参数并且需要实现可选参数(也称为命令行标志或选项)时,事情将变得复杂。
作为这种情况的解决方案,您可以使用内置的 getopts 来使用命名参数。使用以下 shell 脚本,我们可以覆盖一些脚本中的默认值:
#!/bin/bash #Linux迷 www.linuxmi.com title="Linux迷 www.linuxmi.com" message="Hello world!www.linuxmi.com Linux迷" while getopts ":t:m:" option; do echo $option case "${option}" in t) title=${OPTARG} ;; m) message=${OPTARG} ;; esac done zenity --info --title="$title" --text="$message"
默认情况下,上面的脚本显示一个带有默认标题和消息的 GTK 消息框,但是您可以使用命名的命令行参数来覆盖它们,如下所示:
./linuxmi.com.sh -t "hello" ./linuxmi.com.sh -m "world"
getopts 内置支持仅使用单个字母选项。您可以使用 getopt 来使用长形式选项(即–title),如此 gist 所示。
引用传递是一种编程语言特性,它允许您通过内存引用将数据传递到函数中,而不是将整个数据段复制到新变量中。C ++ 程序员总是努力编写性能优先的代码,对于类对象,结构体和字符串,使用引用传递而不是值传递。
如果您使用的是 Bash 4.3 或更新版本,则可以使用名称引用在 shell 脚本中实现引用传递。以下是一个简单的示例代码片段,通过函数更改字符串变量:
#!/bin/bash #Linux迷 www.linuxmi.com function change_str_var() { local str_new="Bash" local -n str_ref=$1 echo "$str_ref -> $str_new" # Python -> Bash str_ref=$str_new } str="Python" change_str_var str echo $str # Bash
上述 change_str_var 函数使用 local 命令创建一个局部的 str_ref 引用,引用全局的 str 变量。然后,它通过覆盖旧字符串值来分配一个新的字符串值。
一些程序员在函数内部使用 echo 命令,并通过命令替换特性调用特定函数以从 Bash 函数返回值(因为原生 Bash return 关键字仅支持返回有效的退出代码)。这会生成另一个子 shell 并消耗更多资源。因此,现在程序员可以使用引用传递并编写性能优先的Bash函数返回,如果他们使用新的 Bash 版本。
Bash 被称为一种无类型命令语言。换句话说,它通常将变量数据处理为字符串,但根据上下文(例如在算术扩展中)进行相应处理。另一方面,Bash 也允许程序员使用类型属性,并提供两种内置的数组类型。即使有了这些功能,我们也不能将 Bash 视为纯动态类型语言,但这些变量属性将Bash置于无类型和动态类型语言之间。
Bash 支持使用整数变量属性将特定变量标记为整数。一旦创建了一个整数变量,当您分配非整数值时,Bash 会发出警告,如下所示:
Bash 还允许使用 declare -r 命令创建常量。每当您的脚本尝试更改常量时,Bash 会在屏幕上打印错误消息。此外,正如我们之前使用的那样,您可以使用 declare 内置函数创建数组。
Bash 还允许您为变量添加一些修饰符属性。例如,您可以创建仅包含小写字母或大写字母的字符串,如下所示:
declare -l lc_str="Hello World" declare -u uc_str uc_str="Hello" uc_str="World" echo $lc_str # hello world echo $uc_str # WORLD
使用 Bash 变量属性,您可以编写更少出错、更易读、更现代的 shell 脚本。
相关:Bash 特殊变量(,?,#,@,$$,$*) 见 https://www.linuxmi.com/bash-special-variables.html
如果还有什么疑问与建议,请在评论栏里给我们留言。感谢您的阅读。
The above is the detailed content of One article to understand what package dependencies are on Linux. For more information, please follow other related articles on the PHP Chinese website!