Blog

无显示读取

Content #

有时你需要从脚本用户处得到输入,但又不想在屏幕上显示输入信息。典型的例子就是输入密码,但除此之外还有很多种需要隐藏的数据。

-s选项可以避免在read命令中输入的数据出现在屏幕上(其实数据还是会被显示,只不过read命令将文本颜色设成了跟背景色一样)。来看一个在脚本中使用-s选项的例子:

$ cat askpassword.sh
#!/bin/bash
# Hiding input data
#
read -s -p "Enter your password: " pass
echo
echo "Your password is $pass"
exit
$
$ ./askpassword.sh
Enter your password:
Your password is Day31Bright-Test
$

屏幕上不会显示输入的数据,但这些数据会被赋给变量,以便在脚本中使用。

From #

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

超时与字符数限制(read命令)

Content #

使用read命令时要当心。脚本可能会一直苦等着用户输入。如果不管是否有数据输入,脚本都必须继续执行,你可以用-t选项来指定一个计时器。-t选项会指定 read命令等待输入的秒数。如果计时器超时,则read命令会返回非0退出状态码:

$ cat asknametimed.sh
#!/bin/bash
# Using the read command with a timer
#
if read -t 5 -p "Enter your name: " name
then
     echo "Hello $name, welcome to my script."
else
     echo
     echo "Sorry, no longer waiting for name."
fi
exit
$
$ ./asknametimed.sh
Enter your name: Christine
Hello Christine, welcome to my script.
$
$ ./asknametimed.sh
Enter your name:
Sorry, no longer waiting for name.
$

我们可以据此(如果计时器超时,则read命令会返回非0退出状态码)使用标准的结构化语句(比如if-then语句或while循环)来轻松地梳理所发生的具体情况。在本例中,计时器超时,if语句不成立,shell执行的是else部分的命令。

...

read命令

Content #

read命令从标准输入(键盘)或另一个文件描述符中接受输入。获取输入后, read命令会将数据存入变量。

$ cat askname.sh
#!/bin/bash
# Using the read command
#
echo -n "Enter your name: "
read name
echo "Hello $name, welcome to my script."
exit
$
$ ./askname.sh
Enter your name: Richard Blum
Hello Richard Blum, welcome to my script.
$

注意,用于生成提示的echo命令使用了-n选项。该选项不会在字符串末尾输出换行符,允许脚本用户紧跟其后输入数据。这让脚本看起来更像表单。

实际上,read命令也提供了-p选项,允许直接指定提示符:

$ cat askage.sh
#!/bin/bash
# Using the read command with the -p option
#
read -p "Please enter your age: " age
days=$[ $age * 365 ]
echo "That means you are over $days days old!"
exit
$
$ ./askage.sh
Please enter your age: 30
That means you are over 10950 days old!
$

你会注意到,当在第一个例子中输入姓名时,read命令会将姓氏和名字保存在同一个变量中。read命令会将提示符后输入的所有数据分配给单个变量。如果指定多个变量,则输入的每个数据值都会分配给变量列表中的下一个变量。如果变量数量不够,那么剩下的数据就全部分配给最后一个变量:

...

使用getopts命令

Content #

getopts(注意是复数)是bash shell的内建命令,和近亲getopt看起来很像,但多了一些扩展功能。

getopt与getopts的不同之处在于,前者在将命令行中选项和参数处理后只生成一个输出,而后者能够和已有的shell位置变量配合默契。

getopts每次只处理一个检测到的命令行参数。在处理完所有的参数后,getopts 会退出并返回一个大于0的退出状态码。这使其非常适合用在解析命令行参数的循环中。

getopts命令的格式如下:

getopts optstring variable

有效的选项字母会在optstring中列出,如果选项字母要求有参数值,就在其后加一个冒号。不想显示错误消息的话,可以在optstring之前加一个冒号。 getopts命令会将当前参数保存在命令行中定义的variable中。

getopts命令要用到两个环境变量。如果选项需要加带参数值,那么OPTARG环境变量保存的就是这个值。OPTIND环境变量保存着参数列表中getopts正在处理的参数位置。这样在处理完当前选项之后就能继续处理其他命令行参数了。

下面来看一个使用getopts命令的简单例子:

$ cat extractwithgetopts.sh
#!/bin/bash
# Extract command-line options and values with getopts
#
echo
while getopts :ab:c opt
do
     case "$opt" in
          a) echo "Found the -a option" ;;
          b) echo "Found the -b option with parameter value $OPTARG";;
          c) echo "Found the -c option" ;;
          *) echo "Unknown option: $opt" ;;
     esac
done
exit
$
$ ./extractwithgetopts.sh -ab BValue -c

Found the -a option
Found the -b option with parameter value BValue
Found the -c option
$

while语句定义了getopts命令,指定要查找哪些命令行选项,以及每次迭代时存储它们的变量名(opt)。

...

使用getopt命令

Content #

  1. 命令格式

getopt命令可以接受一系列任意形式的命令行选项和参数,并自动将其转换成适当的格式。getopt的命令格式如下:

getopt optstring parameters

optstring是这个过程的关键所在。它定义了有效的命令行选项字母,还定义了哪些选项字母需要参数值。

首先,在optstring中列出要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后面加一个冒号。getopt命令会基于你定义的 optstring解析提供的参数。

$ getopt ab:cd -a -b BValue -cd test1 test2
-a -b BValue -c -d -- test1 test2

optstring定义了4个有效选项字母:a、b、c和d。冒号(:)被放在了字母b后面,因为b选项需要一个参数值。当getopt命令运行时,会检查参数列表(-a -b BValue -cd test1 test2),并基于提供的optstring进行解析。注意,它会自动将-cd分成两个单独的选项,并插入双连字符来分隔命令行中额外的参数。

如果optstring未包含你指定的选项,则在默认情况下,getopt命令会产生一条错误消息:

$ getopt ab:cd -a -b BValue -cde test1 test2
getopt: invalid option -- 'e'
 -a -b BValue -c -d -- test1 test2

如果想忽略这条错误消息,可以使用getopt的-q选项:

$ getopt -q ab:cd -a -b BValue -cde test1 test2
 -a -b 'BValue' -c -d -- 'test1' 'test2'

注意,getopt命令选项必须出现在optstring之前。

...

移动参数(shift)

Content #

bash shell工具箱中的另一件工具是shift命令,该命令可用于操作命令行参数。跟字面上的意思一样,shift命令会根据命令行参数的相对位置进行移动。

在使用shift命令时,默认情况下会将每个位置的变量值都向左移动一个位置。因此,变量$3的值会移入$2,变量$2的值会移入$1,而变量$1的值则会被删除(注意,变量$0的值,也就是脚本名,不会改变)。

这是遍历命令行参数的另一种好方法,尤其是在不知道到底有多少参数的时候。你可以只操作第一个位置变量,移动参数,然后继续处理该变量。

$ cat shiftparams.sh
#!/bin/bash
# Shifting through the parameters
echo "Using the shift method:"
count=1
while [ -n "$1" ]
do
     echo "Parameter #$count = $1"
     count=$[ $count + 1 ]
     shift
done
echo
exit
$
$ ./shiftparams.sh alpha bravo charlie delta
Using the shift method:
Parameter #1 = alpha
Parameter #2 = bravo
Parameter #3 = charlie
Parameter #4 = delta

该脚本在while循环中测试第一个参数值的长度。当第一个参数值的长度为0时,循环结束。测试完第一个参数后,shift命令会将所有参数移动一个位置。

注意 使用shift命令时要小心。如果某个参数被移出,那么它的值就被丢弃了,无法再恢复。

另外,也可以一次性移动多个位置。只需给shift命令提供一个参数,指明要移动的位置数即可:

shift 2

From #

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

...

$*变量和$@变量

Content #

\(*变量和\)@变量可以轻松访问所有参数,它们各自包含了所有的命令行参数。

$*变量会将所有的命令行参数视为一个单词。这个单词含有命令行中出现的每一个参数。基本上,$*变量会将这些参数视为一个整体,而不是一系列个体。

$@变量会将所有的命令行参数视为同一字符串中的多个独立的单词,以便你能遍历并处理全部参数。这通常使用for命令完成。

$ cat grabbingallparams.sh
#!/bin/bash
# Testing different methods for grabbing all the parameters
#
echo
echo "Using the \$* method: $*"
echo
echo "Using the \$@ method: $@"
echo
exit
$
$ ./grabbingallparams.sh alpha beta charlie delta
Using the $* method: alpha beta charlie delta
Using the $@ method: alpha beta charlie delta

注意,从表面上看,两个变量产生的输出相同,均显示了所有命令行参数。下面的例子演示了不同之处:

$ cat grabdisplayallparams.sh
#!/bin/bash
# Exploring different methods for grabbing all the parameters
#
echo
echo "Using the \$* method: $*"
count=1
for param in "$*"
do
     echo "\$* Parameter #$count = $param"
     count=$[ $count + 1 ]
done
#
echo
echo "Using the \$@ method: $@"
count=1
for param in "$@"
do
     echo "\$@ Parameter #$count = $param"
     count=$[ $count + 1 ]
done
echo
exit
$
$ ./grabdisplayallparams.sh alpha beta charlie delta

Using the $* method: alpha beta charlie delta
$* Parameter #1 = alpha beta charlie delta

Using the $@ method: alpha beta charlie delta
$@ Parameter #1 = alpha
$@ Parameter #2 = beta
$@ Parameter #3 = charlie
$@ Parameter #4 = delta

通过使用for命令遍历这两个特殊变量,可以看出二者如何以不同的方式处理命令行参数。\(*变量会将所有参数视为单个参数,而\)@变量会单独处理每个参数。这是遍历命令行参数的一种绝妙方法。

...

获取命令行中最后一个参数(${!#})

Content #

特殊变量$#含有脚本运行时携带的命令行参数的个数。

这个变量还提供了一种简便方法来获取命令行中最后一个参数,完全不需要知道实际上到底用了多少个参数。

如果仔细考虑过,你可能会觉得既然$#变量含有命令行参数的总数,那么变量 \({\)#}应该就代表了最后一个位置变量。试试看会发生什么:

$ cat badlastparamtest.sh
#!/bin/bash
# Testing grabbing the last parameter
#
echo The number of parameters is $#
echo The last parameter is ${$#}
exit
$
$ ./badlastparamtest.sh one two three four
The number of parameters is 4
The last parameter is 2648
$

显然,这种方法不管用。这说明不能在花括号内使用$,必须将$换成!。很奇怪,但的确有效:

$ cat goodlastparamtest.sh
#!/bin/bash
# Testing grabbing the last parameter
#
echo The number of parameters is $#
echo The last parameter is ${!#}
exit
$
$ ./goodlastparamtest.sh one two three four
The number of parameters is 4
The last parameter is four
$
$ ./goodlastparamtest.sh
The number of parameters is 0
The last parameter is ./goodlastparamtest.sh
$

当命令行中没有任何参数时,\(#的值即为0,但\){!#}会返回命令行中的脚本名。

...

cook:systemd

启动过程性能分析 #

systemd-analyze time
找出引导过程中花费时间最多的服务
systemd-analyze blame
systemd-analyze verify
systemd-analyze critical-chain

systemd-analyze plot > boot.svg 将引导过程写入SVG格式的文件 systemd-analyze dot graphical.target | dot -Tsvg > graphical.svg

资源使用情况 #

systemd-cgls 以递归形式展示cgroup的结构层次 systemd-cgtop 显示单元的资源使用情况

日志持久化存储 #

systemd-journald默认将日志记录到tmpfs文件系统的/run/log/journal/目录下。若要持久化,需做如下操作:

mkdir -p -m 2775 /var/log/journal
chgrp systemd-journal /var/log/journal
systemctl restart systemd-journald

systemctl #

systemctl list-unit-files -t service
systemctl [list-units] -t service
systemctl [list-units] -at service
systemctl [list-units] --type service --all
systemctl [list-units] --type service --failed
systemctl [list-units] -t service --state=dead
systemctl enable --now httpd
systemctl edit --force --full timestamp.service
systemctl list-dependencies graphical.target
systemctl list-dependencies --after network.target
systemctl get-default
systemctl set-default multi-user
sudo systemctl isolate multi-user

cook:journalctl

...