《linux程序设计》第二章笔记

点(.)命令

下面的例子在命令行中使用点命令,但你完全可以把它用在一个脚本程序中。

假设你有两个包含环境设置的文件,它们分别针对两个不同的开发环境。为了设置老的、经典命令的环境,你可以使用文件classic_set,它的内容如下:

#!/bin/sh

version=classic
PATH=/usr/local/old_bin/usr/bin:/bin:.
PS1="classic> "

对于新命令,使用文件latest_set:

#!/bin/sh
version=latest
PATH=/usr/local/new_bin:/usr/bin:/bin.
PS1=" latest version> "

可以通过将这些脚本程序和点灵敏结合来设置环境,就像下面的实例那样:

$. ./classic_set
classic> echo $version
classic
classic> . /latest_set
latest version> echo $version
latest
latest version>

这个脚本程序使用点命令执行,所以每个脚本程序都是在当前shell中执行。这使得脚本程序可以改变当前shell中的环境设置,即使脚本程序执行结束后,这些改变仍然有效。

eval命令

eval命令允许你对参数进行求值。它是shell的内置命令,通常不会以单独命令的形式存在,使用X/Open规范中的一个小例子来演示它的用法:

foo=10
x=foo
y='$'$x
echo $y

输出$foo。

foo=10
x=foo
eval y='$'$x
echo $y

输出10,因此eval有点像一个额外的$,它给出一个变量的值的值。

eval命令十分有用,它允许代码被随时生成和运行。虽然它的确增加了脚本调试的复杂度,但它可以让你完成使用其他方法难以或者根本无法完成的事情。

exec命令

exec命令有两种不同的用法。它的典型用法是将当前shell替换为一个不同的程序。

例如:

exec wall "Hello"

脚本中这个命令会用wall命令替换当前的shelll。脚本程序中exec命令后面的代码都不会执行,因为执行这个脚本的shell已经不存在了。

exec的第二种用法是修改当前文件描述符:

exec 3< afile

这使得文件描述符3被打开以便从文件afile中读取数据。这种用法非常少见。

trap命令

此处略。

dialog工具

严格来说dialog并不是shell的一部分,但是在通常情况下,它仅仅在shell程序设计中有用。

如果你知道你的脚本程序只需要运行在linux控制台上,就可以使用dialog工具命令。

这个命令使用文本模式的图形和色彩,提供了友好的面向图形的解决方案。

一些linux发行版中默认没有dialog工具,例如ubuntu,可能必须添加公开维护的套件库来找到一个现成的版本。在其他linux发行版中,可能会找到一个已安装的替代工机具gdialog。它和dialog工具非常相似,但它依赖GNOME用户接口来显示其对话框。然而,你得到的回报是你获得了一个真正的图形化界面。一般来说,你可以将任何使用dialog工具的程序中对dialog工具的调用替换为对gdialog工具的调用,从而获得程序的一个图形化版本。

简单示例:

dialog --msgbox "Hello world" 9 18

执行后会在屏幕显示一个图形化的消息框。

下面对dialog的各种可能性进行详细的介绍:

类型用于创建类型的选项含义
复选框–checklist允许用户显示一个选项列表,每个选项都可以被单独选择
信息框–infobox在显示消息后,对话框将立刻返回,但不清除屏幕
输入框–inputbox允许用户输入文本
菜单框–menu允许用户选择列表中的一项
消息框–msgbox向用户显示一条消息,同时显示一个OK按钮,用户可以通过选择按钮继续操作
单选框–radiolist允许用户选择列表中的一个选项
文本框–textbox允许用户在带有滚动条的文本框中显示一个文件的内容
是/否框–yesno允许用户提问,用户可以选择yes或no

如果想获得任何类型的允许文本输入或进行选择的对话框的输出,你必须捕获标准错误流。通常是把它指向某个临时文件以便后续处理。

要想获得Yes/No对话框的输出结果,只需查看它的退出码,返回0表示成功,1表示失败。

所有的对话框类型都有各种各样的用于控制的参数(见下表),比如控制显示的对话框的大小和形状。我们首先列出每种类型所需的参数,然后在命令行上演示其中一部分参数的用法。最后,你将看到一个简单的将几种对话框结合起来的程序。

对话框类型参数
–checklisttext height width list-height [tag text status]…
–infoboxtext height width
–inputboxtext height width [initial string]
–menutext height width menu-height [tag item] …
–msgboxtext height width
–radiolisttext height width list-height [tag text status]…
–textboxfilename height width
–yesnotext height width

除此之外,所有的对话框类型都有几个相同的参数选项。在此只介绍两个,–title和–clear。前者用于指定对话框的标题,后者用来完成清屏操作。

示例
dialog --title "Check me" --checklist "Pick Numbers" 15 25 3 1 "one" "off" 2 "two" "on" 3 "three" "off"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5c3gkQeU-1662206492329)(vx_images/71555790826696.png)]

为了能够将这些放在一个程序中,你需要能够访问用户输入的结果。这一点很容易实现,对于文本输入,只需要重定向标准错误流或检查环境变量$?的内容,$?的值实际上就是前一个命令的退出状态。

示例2
  1. 首先,这个程序通过一个简单的对话框来告诉用户发生的事情。你不需要获得返回值或任何用户的输入,所以这看起来非常简单和友好。

    #!/bin/bash
    # ASK some questions and collect the answer
    dialog --title "Questionnaire" --msgbox "Welcome to my simple survey" 9 18
    
  2. 然后用一个简单的yes/no对话框来询问用户是否要继续操作。我们用环境变量$?来检查用户是否选择了yes(返回码是0)。如果用户不想继续操作,就使用一个简单的信息框显示信息,信息框在退出之前不需要用户的输入。

    dialog --title "Confirm" --yesno "Are you willing to take part?" 9 18
    if [ $? != 0 ];then
        dialog --infobox "Thank you anyway" 5 20
        sleep 2
        dialog --clear
        exit 0
    fi
    
  3. 我们使用一个输入框来询问用户的姓名。重定向标准错误流2到临时文件_1.txt,然后再将它放到变量Q_NAME中:

    dialog --title "Questionnaire" --inputbox "Please enter your name" 9 30 2>_1.txt
    
    Q_NAME=$(cat _1.txt)
    
  4. 现在显示一个菜单,它有4个不同的选项。你再次重定向标准错误流并且将它装载到一个变量中:

    dialog --menu "$Q_NAME, what music do you like best?" 15 30 4 1 "Classical" 2 "Jazz" 3 "Country" 4 "Other" 2>_1.txt
    Q_MUSIC=$(cat _1.txt)
    
  5. 用户选择的菜单项编号将被保存到临时文件_1.txt中,同时这个结果被放入变量Q_MUSIC中,以便你对结果进行测试。

    if [ "$Q_MUSIC" = "1" ];then
        dialog --title "Likes Classical" --msgbox "Good choice!" 12 25
    else
        dialog --title "Doesn't like Classical" --msgbox "Shame" 12 25
    fi
    
  6. 最后,清除对话框并退出程序

    dialog --clear
    exit 0
    

这里给出上面示例的完整代码,你可以选择dialog或者gdialog:

#!/bin/bash

gui=dialog
#gui=gdialog

#1.告诉用户发生的事情
$gui --title "Questionnaire" --msgbox "Welcome to my simple survey" 9 18

#2.询问是否继续操作
$gui --title "Confirm" --yesno "Are you willing to take part?" 9 18
if [ $? != 0 ];then
   $gui --infobox "Thank you anyway" 5 20
   sleep 2
   $gui --clear
   exit 0
fi

#3.询问用户姓名
$gui --title "Questionnaire" --inputbox "Please enter your name" 9 30 2>_1.txt
Q_NAME=$(cat _1.txt)

#4.让用户选择最爱的音乐
$gui --menu "$Q_NAME, what music do you like best?" 15 30 4 \
1 "Classical" \
2 "Jazz" \
3 "Country" \
4 "Other" \
2>_1.txt

Q_MUSIC=$(cat _1.txt)

#5.对选择的结果测试
if [ "$Q_MUSIC" = "1" ];then
   $gui --title "Likes Classical" --msgbox "Good choice!" 12 25
else
   $gui --title "Doesn't like Classical" --msgbox "Shame" 12 25
fi
$gui --clear
rm -rf _1.txt
exit 0
shell实战综合应用
需求

有许多CD唱片,你将设计和实现一个管理CD唱片的程序。

设计

由于所有需要存储的数据全部都是文本,而且假设唱片不是很多,因此没有必要使用一个复杂的数据库,使用一些简单的文本文件即可。将资料保存在文本文件中比较简单,而且如果你的需求发生了变化,操纵文本文件总是要比操纵其他类型的文件更加容易。

如果对曲目数量没有限制,就有以下3中选择。

  • 只使用一个文件,用一行来保存“标题”信息,再用n行来保存该CD唱片上的曲目信息。
  • 将每张CD唱片的所有信息都放置在一行上,允许该行一直延续知道没有曲目信息需要保存为止。
  • 把标题信息和曲目分开,用不同的文件来分别保存它们。

只有第三种做法能够让你灵活地修改文件的格式,如果今后你想要把数据库转换为关系数据库格式的话,你就需要修改文件格式,因此选择第三种方法。

下一个决策时要在文件里放入哪些信息、

我们决定对每张CD保存以下信息:

  • CD唱片里的目录编号
  • 标题
  • 曲目类型
  • 作曲家

对曲目,只保存两条信息:

  • 曲目编号
  • 曲名

为了把这两个文件结合起来,必须把曲目信息和CD唱片上的其他信息关联起来。为此,你需要使用CD唱片的目录编号。因为它对每张CD唱片都是唯一的,所以它在标题文件中只出现一次,在曲目文件中对每首曲目只出现一次。

让我们来看一个示例标题文件:

目录编号标题曲目类型作曲家
CD123Cool sax爵士Bix
CD214Classic violin古典Bach
CD345Hits99流行Various

它对应的曲目文件:

目录编号曲目编号曲名
CD1231Some jazz
CD1232More jazz
CD2341Sonata in D minor
CD3451Dizzy

这两个文件通过目录编号结合在一起。标题文件中的一个数据项一般都对应曲目文件中的多行数据。
你需要决定的最后一件事情是如何分隔数据项。在关系数据库里,长度固定的数据字段比较常见,但他并非总是最方便的。另一种常见的方法是使用逗号,这个例子就选择了这个方法(即用逗号分隔变量,或CSV文件)。

这里先列出下面“实验”部分要用到的函数:

get_return()
get_confirm()
set_menu_choice()
insert_title()
insert_track()
add_record_tracks()
add_records()
find_cd()
update_cd()
count_cds()
remove_records()
list_tracks()
  1. 首先要设置好脚本要使用的一些全局变量,包括标题文件、曲目文件和一个临时文件。我们还设置Ctrl+C组合键中断处理,以确保在用户在中断脚本程序时删除临时文件:

    menu_choice=""
    current_cd=""
    title_file="title.cdb"
    tracks_file="tracks.cdb"
    temp_file=/tmp/cdb.$$
    trap 'rm -f $temp_file' EXIT
    
  2. 现在开始定义函数。因为脚本程序是从文件的第一行开始执行,所以这样做可以确保在调用任何一个函数之前都能够找到它的定义。为了避免在几个地方反复编写同样代码,最开始的两个函数是简单的工具型函数。

get_return(){
	echo -e "Press return \c"
	read x
	return 0
}
get_confirm(){
	echo -e "Are you sure?"
	while true
	do
		read x
		case "$x" in
			y | yes | Y | Yes | YES)
				return 0;;
			n | no 	| N | No  | no)
				echo 
				echo "Cancelled"
				return 1;;
			*)	echo "Please enter yes or no";;
		esac
	done
}
  1. 接下来是主菜单函数set_menu_choice。菜单的内容是动态变化的,当用户选择了某张CD唱片后,主菜单会多出几个选项。
    注意:echo -e命令可能不能移植到某些shell’中。

    set_menu_choice(){
        clear
        echo "Options :-"
        echo
        echo "	a) Add new CD"
        echo "	f) Find cd"
        echo "	c) Count the CDs and tracks in the catalog"
        if [ "$cdcatnum" != "" ];then
    	    echo "	l) List tracks on $cdtitle"
    	    echo "	r) Remove $cdtitle"
    	    echo "	u) Update track information for $cdtitle"
        fi
        echo "	q) Quit"
        echo
        echo -e "Please enter choice then press return \c"
        read menu_choice
        return
    }
    
  2. 接下来是两个很短小的函数insert_title和insert_track,它们用于向数据库文件里添加数据,虽然有的人不喜欢这种长度只有一行的函数,但它们有助于让其他函数的含义更加清晰易懂。
    紧跟着是一个比较大的函数add_record_trakcs,他会用到上述两个短小的函数。这两个函数使用模式匹配来确保用户未输入逗号(因为我们把逗号用作数据字段之间的分隔符),使用算术操作在用户输入曲目时递增当前曲目的编号:

  3. add_records函数用于输入新CD唱片的标题信息:

add_records(){
	#Prompt for the initial information
	echo -e "Enter catalog name \c"
	read tmp
	cdcatnum=${tmp%%,*}

	echo -e "Enter title \c"
	read tmp
	cdtitle=${tmp%%,*}

	echo -e "Enter type \c"
	read tmp
	cdtype=${tmp%%,*}

	echo -e "Enter artist/composer\c"
	read tmp
	cdac=${tmp%%,*}

	#Check that they want to enter the information

	echo "About to add new entry"
	echo "$cdcatnum then append it to the title file"

	#if confirmed then append it to the titles file
	if get_confirm; then
		insert_title $cdcatnum, $cdtitle, $cdtype, $cdac
		add_record_tracks
	else
		remove_records
	fi
}
  1. find_cd函数的作用是使用grep命令在唱片标题文件中查找CD唱片的有关资料。你需要知道查询字符串在标题文件里出现的次数,但grep命令的返回值只会告诉我们匹配了0次或者多次。我们将grep命令的输出保存到一个临时文件中,文件中的每行对应一次匹配,然后再统计该文件的行数。
  • 待办
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

barbyQAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值