Blog

使用数据字段变量

Content #

gawk的主要特性之一是处理文本文件中的数据。它会自动为每一行的各个数据元素分配一个变量。在默认情况下,gawk会将下列变量分配给文本行中的数据字段。

·$0代表整个文本行。·$1代表文本行中的第一个数据字段。·$2代表文本行中的第二个数据字段。·$n代表文本行中的第n个数据字段。

文本行中的数据字段是通过字段分隔符来划分的。在读取一行文本时,gawk会用预先定义好的字段分隔符划分出各个数据字段。在默认情况下,字段分隔符是任意的空白字符(比如空格或制表符)。

在下面的例子中,gawk脚本会读取文本文件,只显示第一个数据字段的值:

$ cat data2.txt
One line of test text.
Two lines of test text.
Three lines of test text.
$
$ gawk '{print $1}' data2.txt
One
Two
Three
$

该脚本使用$1字段变量来显示每行文本的第一个数据字段。如果要读取的文件采用了其他的字段分隔符,可以通过-F选项指定:

$ gawk -F: '{print $1}' /etc/passwd
root
daemon
bin
[...]
christine
sshd
$

这个简短的脚本显示了系统中密码文件的第一个数据字段。由于/etc/passwd文件使用冒号(:)来分隔数据字段,因此要想划出数据字段,就必须在gawk选项中将冒号指定为字段分隔符(-F:)。

From #

Linux命令行与shell脚本编程大全

使用select命令创建菜单

Content #

创建文本菜单的一半工夫花在了建立菜单布局和获取用户输入上。bash shell提供了一款易于上手的小工具,能够帮助我们自动完成这些工作。

select命令只需要一个命令就可以创建出菜单,然后获取输入并自动处理。 select命令的格式如下:

select variable in list
do
    commands
done

list参数是由空格分隔的菜单项列表,该列表构成了整个菜单。select命令会将每个列表项显示成一个带编号的菜单项,然后显示一个由PS3环境变量定义的特殊提示符,指示用户做出选择。

下面是一个select命令的简单示例:

$ cat smenu1
#!/bin/bash
# using select in the menu

function diskspace {
   clear
   df -k
}

function whoseon {
   clear
   who
}

function memusage {
   clear
   cat /proc/meminfo
}

PS3="Enter option: "
select option in "Display disk space" "Display logged on users" ~CA
"Display memory usage" "Exit program"
do
   case $option in
   "Exit program")
         break ;;
   "Display disk space")
         diskspace ;;
   "Display logged on users")
         whoseon ;;
   "Display memory usage")
         memusage ;;
   *)
         clear
         echo "Sorry, wrong selection";;
   esac
done
clear
$

select语句中的所有内容必须作为一行出现。这从续行符就可以看出。运行该脚本会自动生成如下菜单: $ ./smenu1

...

使用shtool函数

Content #

可以在命令行或shell脚本中直接使用shtool函数。下面是一个在shell脚本中使用platform函数的例子:

$ cat test16
#!/bin/bash

shtool platform
$ ./test16
Ubuntu 20.04 (AMD64)
$

platform函数会返回Linux发行版以及系统所使用CPU硬件的相关信息。

我很喜欢prop函数。它使用\、|、/和-字符创建了一个旋转的进度条,可以告诉 shell脚本用户目前正在进行一些后台处理工作。要使用prop函数,只需将希望监看的输出管接到shtool脚本即可:

$ ls -al /usr/bin | shtool prop -p "waiting..."
waiting...
$

prop函数会在处理过程中不停地变换进度条字符。在本例中,输出信息来自ls命令。你能看到多少进度条取决于CPU能以多快的速度列出/usr/bin目录中的文件。-p选项可以设置出现在进度条之前的文本。

From #

Linux命令行与shell脚本编程大全

函数递归计算阶乘(Bash)

Content #

局部函数变量的一个特性是自成体系(self-containment)。除了获取函数参数,自成体系的函数不需要使用任何外部资源。

递归算法的经典例子是计算阶乘。 5! = 1 * 2 * 3 * 4 * 5 = 120 使用递归,这一算法可以简化为以下形式: x! = x * (x-1)! 阶乘函数用其自身计算阶乘的值:

$ cat test13
#!/bin/bash
# using recursion

function factorial {
   if [ $1 -eq 1 ]
   then
      echo 1
   else
      local temp=$[ $1 - 1 ]
      local result=$(factorial $temp)
      echo $[ $result * $1 ]
   fi
}

read -p "Enter value: " value
result=$(factorial $value)
echo "The factorial of $value is: $result"
$
$ ./test13
Enter value: 5
The factorial of 5 is: 120
$

From #

Linux命令行与shell脚本编程大全

...

从函数返回数组

Content #

函数向shell脚本返回数组变量也采用类似的方法。函数先用echo语句按正确顺序输出数组的各个元素,然后脚本再将数组元素重组成一个新的数组变量:

$ cat test12
#!/bin/bash
# returning an array value

function arraydblr {
   local origarray
   local newarray
   local elements
   local i
   origarray=($(echo "$@"))
   newarray=($(echo "$@"))
   elements=$[ $# - 1 ]
   for (( i = 0; i <= $elements; i++ ))
   {
      newarray[$i]=$[ ${origarray[$i]} * 2 ]
   }
   echo ${newarray[*]}
}

myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
arg1=$(echo ${myarray[*]})
result=($(arraydblr $arg1))
echo "The new array is: ${result[*]}"
$
$ ./test12
The original array is: 1 2 3 4 5
The new array is: 2 4 6 8 10

该脚本通过$arg1变量将数组元素作为参数传给arraydblr函数。arraydblr函数将传入的参数重组成新的数组变量,生成该数组变量的副本。然后对数据元素进行遍历,将每个元素的值翻倍,并将结果存入函数中的数组变量副本。

...

向函数传递数组

Content #

向脚本函数传递数组变量的方法有点儿难以理解。将数组变量当作单个参数传递的话,它不会起作用:

$ cat badtest3
#!/bin/bash
# trying to pass an array variable

function testit {
   echo "The parameters are: $@"
   thisarray=$1
   echo "The received array is ${thisarray[*]}"
}

myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
testit $myarray
$
$ ./badtest3
The original array is: 1 2 3 4 5
The parameters are: 1
The received array is 1
$

如果试图将数组变量作为函数参数进行传递,则函数只会提取数组变量的第一个元素。

要解决这个问题,必须先将数组变量拆解成多个数组元素,然后将这些数组元素作为函数参数传递。最后在函数内部,将所有的参数重新组合成一个新的数组变量。来看下面的例子:

$ cat test10 #!/bin/bash

function testit { local newarray newarray=(`echo “$@”`) echo “The new array value is: ${newarray[*]}” }

...

local关键字(Bash)

Content #

任何在函数内部使用的变量都可以被声明为局部变量。为此,只需在变量声明之前加上local关键字即可:

local temp

也可以在变量赋值语句中使用local关键字:

local temp=$[ $value + 5 ]

local关键字保证了变量仅在该函数中有效。如果函数之外有同名变量,那么 shell会保持这两个变量的值互不干扰。这意味着你可以轻松地将函数变量和脚本变量分离开,只共享需要共享的变量

From #

Linux命令行与shell脚本编程大全

使用函数输出

Content #

可以将函数的输出保存到shell变量中:

result=$(dbl)

这个命令会将dbl函数的输出赋给$result变量。来看一个例子:

$ cat test5b
#!/bin/bash
# using the echo to return a value

function dbl {
   read -p "Enter a value: " value
   echo $[ $value * 2 ]
}
result=$(dbl)
echo "The new value is $result"
$
$ ./test5b
Enter a value: 200
The new value is 400
$
$ ./test5b
Enter a value: 1000
The new value is 2000
$

新函数会用echo语句来显示计算结果。该脚本会获取dbl函数的输出,而不是查看退出状态码。

这个例子演示了一个不易察觉的技巧。注意,dbl函数实际上输出了两条消息。 read命令输出了一条简短的消息来向用户询问输入值。bash shell脚本非常聪明,并不将其作为STDOUT输出的一部分,而是直接将其忽略。如果用echo语句生成这条消息来询问用户,那么它会与输出值一起被读入shell变量。

这种方法还可以返回浮点值和字符串,这使其成为一种获取函数返回值的强大方法。

From #

Linux命令行与shell脚本编程大全

...

使用return命令

Content #

return命令允许指定一个整数值作为函数的退出状态码:

$ cat test5
#!/bin/bash
# using the return command in a function

function dbl {
   read -p "Enter a value: " value
   echo "doubling the value"
   return $[ $value * 2 ]
}

dbl
echo "The new value is $?"
$

dbl函数会将\(value变量中用户输入的整数值翻倍,然后用return命令返回结果。脚本用\)?变量显示出该结果。

当用这种方法从函数中返回值时,一定要小心。为了避免出问题,牢记以下两个技巧。

  1. 函数执行一结束就立刻读取返回值。
  2. 退出状态码必须介于0~255。

如果在用\(?变量提取函数返回值之前执行了其他命令,那么函数的返回值会丢失。记住,\)?变量保存的是最后执行的那个命令的退出状态码。

第二个技巧界定了返回值的取值范围。由于退出状态码必须小于256,因此函数结果也必须为一个小于256的整数值。大于255的任何数值都会产生错误的值: $ ./test5 Enter a value: 200 doubling the value The new value is 1 $ 如果需要返回较大的整数值或字符串,就不能使用return方法。

From #

Linux命令行与shell脚本编程大全