命令行之巅:Linux Shell编程的至高艺术(中)

文章一览

  • 前言
  • 一、输入/输出及重定向命令
    • [1.1 输入/输出命令](#1.1 输入/输出命令)
      • [1.1.1 read命令](#1.1.1 read命令)
      • [1.1.2 echo命令](#1.1.2 echo命令)
    • [1.2 输入/输出重定向](#1.2 输入/输出重定向)
    • [1.3 重定向深入讲解](#1.3 重定向深入讲解)
    • [1.4 Here Document](#1.4 Here Document)
      • [1.4.1 /dev/null 文件](#1.4.1 /dev/null 文件)
  • 二、shell特殊字符和命令语法
    • [2.1 引号](#2.1 引号)
      • [2.1.1 双引号](#2.1.1 双引号)
      • [2.1.2 单引号](#2.1.2 单引号)
      • [2.1.3 倒引号](#2.1.3 倒引号)
    • [2.2 注释、管道线和后台命令](#2.2 注释、管道线和后台命令)
    • [2.3 命令执行操作符](#2.3 命令执行操作符)
    • [2.4 复合命令](#2.4 复合命令)
  • 三、shell脚本调试
    • [3.1 解决环境设置问题](#3.1 解决环境设置问题)
    • [3.2 解决脚本错误](#3.2 解决脚本错误)
  • 四、文件包含

前言

在Shell编程中,重定向和特殊字符是非常重要的概念。它们允许我们控制输入和输出,并使我们能够构建复杂的脚本和命令。通过重定向和特殊字符,我们可以将命令的输出保存到文件中,从文件中读取输入,以及将多个命令连接在一起执行。这些功能极大地增强了Shell编程的灵活性和实用性。

重定向是将命令的输入或输出从默认位置改变到指定的位置的过程。在命令行中,我们可以使用特殊字符来指定要进行重定向的位置。例如,使用>字符将命令的输出重定向到文件中。这使得我们可以将命令的结果保存到文件中,而不是在终端上显示出来。类似地,使用<字符可以将文件中的内容重定向为命令的输入。这使得我们可以从文件中读取数据,而不是手动输入。此外,还可以使用>>字符将输出追加到文件中,而不是覆盖原有内容。

本篇文章将会讲解重定向 以及一些特殊字符与命令

一、输入/输出及重定向命令

1.1 输入/输出命令

1.1.1 read命令

利用read命令从键盘上读取数据,然后赋给指定的变量。

一般格式: read [-u fd] [-n nchars] [name1 name2...]

变量个数M与输入数据N个数之间的关系:

例:(判断闰年与平年)

shell 复制代码
#!/bin/bash
#determing if a year is a leap year
echo  "Input a year number"                         #提示输入一个年号
read  year                                                     #读取输入的年号
let  "leap=year%4==0&&year%100!=0||year%400==0"     #计算给定年号是闰年吗?
if [ $leap --eq 0 ]                                            #若leap等于0,则不是闰年
then  echo  "$year is not a leap year. "         #输出不是闰年信息
else  echo  " $year is a leap year. "              #否则,输出闰年信息
fi 

1.1.2 echo命令

显示其后的变量值或者直接显示它后面的字符串。

一般格式: echo [-neE] [arg...]

-e 选项代表不换行

shell 复制代码
$ echo  -e  "Enter  the  file  name  ->\c" 
Enter  the  file  name  ->$ ▌

这种形式与带"-n"选项的命令行功能相同:

-n 选项代表解释转义符

shell 复制代码
$ echo  -n  "Enter  the  file  name  ->"
Enter  the  file  name  ->$ ▌

1.2 输入/输出重定向

1.输入重定向符

一般形式是:命令 < 文件名

让命令(或可执行程序)从指定文件中取得输入数据。例如:

\$ score < file1

注意

bash 复制代码
$ wc -l zhangyu
2 zhangyu
$ wc -l < zhangyu
2

上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。

2.输出重定向符

一般形式是:命令 > 文件名

把命令(或可执行程序)执行的结果输出到指定的文件。例如:

\$ who > abc

3.输出附加定向符

一般形式是: 命令>>文件名

把命令(或可执行程序)的输出附加到指定文件的后面,而该文件原有内容不被破坏。例如:

$ date>> file2

注意:需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

1.3 重定向深入讲解

一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:

  • 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。

  • 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。

  • 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。

    command1 < infile > outfile

默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。

1.4 Here Document

Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。

bash 复制代码
command << delimiter  
    document  
delimiter

它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。

注意

结尾的delimiter 一定要顶格 写,前面不能有任何字符 ,后面也不能有任何字符,包括空格和 tab 缩进。

开始的delimiter前后的空格会被忽略掉

bash 复制代码
wc -l << EOF  
    欢迎来到  
    章鱼大数据  
    www.ipieuvre.com  
EOF  

1.4.1 /dev/null 文件

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:

bash 复制代码
command > /dev/null  

/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。

如果希望屏蔽 stdout 和 stderr,可以这样写:

bash 复制代码
command > /dev/null 2>&1  
sudo apt-get update  > /dev/null 2>&1

二、shell特殊字符和命令语法

2.1 引号

2.1.1 双引号

由双引号括起来的字符(除$、倒引号(`)和转义字符(\)外)均作为普通字符对待。

shell 复制代码
$ cat  exam9
echo  "current  directory  is  ` pwd ` "      #倒引号表示命令替换
echo  "home  directory  is  $HOME"       #以HOME的值代替$HOME
echo  "file*.?"                                          #其中的字符都作为普通字符出现
echo  "directory  '$HOME ' "                   #单引号仍作为普通字符出现
echo  "Filename  is  No\$\* "                   #其中有转义字符
$ exam9
current  directory  is  /home/ciee/dir1
home  directory  is  /home/ciee
file*.?
directory  '/home/ciee'
Filename  is No$\* 

2.1.2 单引号

由单引号括起来的所有字符都作为普通字符出现。例如:

shell 复制代码
$ str=' echo  "directory  is  $HOME" '
$ echo  $str
echo  "directory  is  $HOME "

2.1.3 倒引号

用倒引号括起来的字符串被shell解释为命令行。

可以将一个命令的执行结果赋给变量,即命令替换

一般形式是:变量名=`命令表` 例如:dir=`pwd`

另一种形式是:变量名=(命令表) 例如:dir=(pwd)

shell 复制代码
$ users=` who | wc -l `
$ echo  The  number  of  users  is  $users
The  number  of  users  is  3
$ Nuser=`echo  The  number  of  users  is  \` who | wc -l \` `
$ echo  $Nuser
The  number  of  users  is  3

2.2 注释、管道线和后台命令

1.注释

  • shell程序中以"#"开头的正文行表示注释。

  • #! /bin/bash 说明该脚本是用Bourne Again shell编写的,应该调用相应的解释程序予以执行。

2.管道线

$ ls -l $HOME | wc --l

执行时,前一个命令的输出正好是下一个命令的输入。

$ ls | grep m?.c | wc --l

3.后台命令

$ gcc m1.c m2.c -o prog&

$

后台进程的调度优先级都低于前台进程的优先级

2.3 命令执行操作符

1.顺序执行

各条命令之间以分号(;)隔开,从左到右依次执行。例如:

\$ pwd ; who | wc -l ; cd /home/bin

2.逻辑与

一般形式:命令1 && 命令2

先执行命令1,如果成功,才执行命令2;否则,不执行命令2。例如:

\$ cp ex1 ex10 && rm ex1

3.逻辑或

一般形式:命令1 || 命令2

先执行命令1,如果不成功,则执行命令2;否则,不执行命令2。例如:

$ cat abc || pwd

用&&或||可以把多个命令联系起来

2.4 复合命令

1.{ }形式

以{ }括起来的全部命令可视为语法上的一条命令,出现在管道符的一边 。成组命令的执行顺序是根据命令出现的先后次序由左至右执行

$ { echo "User Report for date.";who; }

应注意,左括号"{"后面应有一个空格右括号"}"之前应有一个分号(;)。

花括号也可以包含若干单独占一行的命令,例如:

shell 复制代码
{  echo  "Report  of  users  for  ` date ` . "
echo
echo  "There  are  ` who | wc -l `  users  logged  in. "
echo
who | sort ; } 

2.( )形式

shell 复制代码
(echo  "Current  directory  is  ` pwd ` . "
cd  /home/cieeqc ;  ls -l ;
cp  m1  em1 && rm  m1
cat  em1) 

二者执行过程相同,但存在重要区别:{ }成组命令只在本shell内执行命令表 ,不产生新的进程;而()成组命令在新的子shell内执行,要建立新的子进程。

shell 复制代码
$ a="current  value "
$ echo  $a
current  value
$ ( a="new  value-1 " ;  echo  $a )
new  value-1                                                 #(子shell内部a的值)
$ echo  $a
current  value                                              # (与前者不同,这是外部a的值)
$ {  a="new  value-2 " ; echo  $a ;  }
new  value-2                                                #(a的新值)
$ echo  $a
new  value-2                                                #(同一进程,a的值也相同)
$ pwd
/home/ciee /dir1
$ (cd  /bin ;  pwd )                                        #(在子shell中将工作目录改为/bin)
/bin
$ pwd
/home/ciee /dir1                                     #(仍是原来的目录,不受上面命令影响)
$ {  cd  /bin ;  pwd ;  }
/bin
$ pwd
/bin                                                              #(同一进程,前后关联)

三、shell脚本调试

3.1 解决环境设置问题

环境设置不对通常包括以下情况:

​ ① 不能直接在其他shell下运行bash脚本。解决的办法是在脚本的第一行写上:#!/bin/bash。

​ ② 在PATH环境变量中没有包括"."(当前目录)。解决办法是设置PATH:PATH=$PATH:.

​ ③ 脚本文件与已存在命令的名字相同。

3.2 解决脚本错误

基本的错误类型有两种:语法错误逻辑错误

  • 语法错误是编写程序时违反了所用编程语言的规则而造成的。
  • 逻辑错误通常是由于程序的逻辑关系存在问题。对此类问题需要进行程序调试。

调试技巧------设置x标志:

  • 使用set -x命令:设置x标志后,执行下面命令前先显示该行的内容。

  • 使用bash --x命令:在启动shell时,将它设置成跟踪模式。

四、文件包含

Shell和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。

Shell 文件包含的语法格式如下:

bash 复制代码
. filename   # 注意点号(.)和文件名中间有一空格  
  
#或  
  
source filename  
相关推荐
MUTA️19 分钟前
AutoDL服务器深度学习使用过程
服务器·人工智能·深度学习·计算机视觉
m0_7482347121 分钟前
Jo-im开发:用于WebRTC的ICE中继服务器Coturn搭建
运维·服务器·webrtc
学Linux的语莫38 分钟前
linux中,redis分布式集群搭建
linux·redis·分布式·mysql
MaiTube&Maipdf1 小时前
对外发PDF设置打开次数
运维·服务器·网络
ppo_wu1 小时前
Ubuntu 24.04.1 LTS 配置静态固定IP地址
linux·tcp/ip·ubuntu·运维开发
真想骂*1 小时前
如何在Linux上配置SSH密钥以实现免密登录
linux·运维·ssh
webmote1 小时前
Fabric.js 入门教程:扩展自定义对象的完整实践(V6)
运维·javascript·canvas·fabric·绘图
lory代码搬运工1 小时前
解决PDF.js部署到IIS服务器上后报错mjs,.ftl 404 (Not Found)
运维·服务器·pdf
云云3211 小时前
云手机:Instagram 账号管理新机遇
服务器·线性代数·安全·智能手机·矩阵
享受 生活1 小时前
【docker】docker desktop 在windows上支持 host模式
运维·docker·容器