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


    点(.)命令

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

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

    #!/bin/sh
    
    version=classic
    PATH=/usr/local/old_bin/usr/bin:/bin:.
    PS1="classic> "
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    #!/bin/sh
    version=latest
    PATH=/usr/local/new_bin:/usr/bin:/bin.
    PS1=" latest version> "
    
    • 1
    • 2
    • 3
    • 4

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

    $. ./classic_set
    classic> echo $version
    classic
    classic> . /latest_set
    latest version> echo $version
    latest
    latest version>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    eval命令

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

    foo=10
    x=foo
    y='$'$x
    echo $y
    
    • 1
    • 2
    • 3
    • 4

    输出$foo。

    foo=10
    x=foo
    eval y='$'$x
    echo $y
    
    • 1
    • 2
    • 3
    • 4

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

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

    exec命令

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

    例如:

    exec wall "Hello"
    
    • 1

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

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

    exec 3< afile
    
    • 1

    这使得文件描述符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
    
    • 1

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

    下面对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"
    
    • 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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
      
      • 1
      • 2
      • 3
    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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    3. 我们使用一个输入框来询问用户的姓名。重定向标准错误流2到临时文件_1.txt,然后再将它放到变量Q_NAME中:

      dialog --title "Questionnaire" --inputbox "Please enter your name" 9 30 2>_1.txt
      
      Q_NAME=$(cat _1.txt)
      
      • 1
      • 2
      • 3
    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)
      
      • 1
      • 2
    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
      
      • 1
      • 2
      • 3
      • 4
      • 5
    6. 最后,清除对话框并退出程序

      dialog --clear
      exit 0
      
      • 1
      • 2

    这里给出上面示例的完整代码,你可以选择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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    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
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    1. find_cd函数的作用是使用grep命令在唱片标题文件中查找CD唱片的有关资料。你需要知道查询字符串在标题文件里出现的次数,但grep命令的返回值只会告诉我们匹配了0次或者多次。我们将grep命令的输出保存到一个临时文件中,文件中的每行对应一次匹配,然后再统计该文件的行数。
    • 待办
  • 相关阅读:
    智慧水务解决方案-最新全套文件
    每日一题——打印菱形图案
    R语言ggplot2可视化:使用patchwork包(直接使用加号+)将两个ggplot2可视化结果横向组合起来形成单个可视化结果图
    动手学深度学习(三)---Softmax回归
    【性能测试】中间件优化
    2023 Google 开发者大会|Mobile开发专题追踪
    C Primer Plus(6) 中文版 第8章 字符输入/输出和输入验证 8.3 结束键盘输入
    solana 入门 1
    基于人工表面等离激元周期调制的漏波天线设计
    ROS报错:joint-state-publisher报错
  • 原文地址:https://blog.csdn.net/qq_51470638/article/details/126681794