分类 技术 下的文章

系统管理员的任务真的很艰难,因为他/她必须监控服务器、用户、日志,还得创建备份,等等等等。对于大多数重复性的任务,大多数管理员都会写一个自动化脚本来日复一日地重复这些任务。这里,我们已经写了一个shell脚本给大家,用来自动化完成系统管理员所要完成的常规任务,这可能在多数情况下,尤其是对于新手而言十分有用,他们能通过该脚本获取到大多数的他们想要的信息,包括系统、网络、用户、负载、内存、主机、内部IP、外部IP、开机时间等。

我们已经注意并进行了格式化输出(在一定程度上哦)。此脚本不包含任何恶意内容,并且它能以普通用户帐号运行。事实上,我们也推荐你以普通用户运行该脚本,而不是root。

Linux Server Health Monitoring

监控Linux系统健康的Shell脚本

在保留Tecmint和脚本作者应得荣誉的前提下,可以自由使用/修改/再分发下面代码。我们已经试着在一定程度上自定义了输出结果,除了要求的输出内容外,其它内容都不会生成。我们也已经试着使用了那些Linux系统中通常不使用的变量,这些变量应该是可以随便用的。

最小系统要求

你所需要的一切,就是一台正常运转的Linux机器。

依赖性

对于一个标准的Linux发行版,使用此软件包不需任何依赖。此外,该脚本不需要root权限来执行。但是,如果你想要安装,则必须输入一次root密码。

安全性

我们也关注到了系统安全问题,所以在安装此包时,不需要安装任何额外包,也不需要root访问权限来运行。此外,源代码是采用Apache 2.0许可证发布的,这意味着只要你保留Tecmint的版权,你可以自由地编辑、修改并再分发该代码。

如何安装和运行脚本?

首先,使用wget命令下载监控脚本“tecmint_monitor.sh”,给它赋予合适的执行权限。

# wget http://tecmint.com/wp-content/scripts/tecmint_monitor.sh
# chmod 755 tecmint_monitor.sh

强烈建议你以普通用户身份安装该脚本,而不是root。安装过程中会询问root密码,并且在需要的时候安装必要的组件。

要安装“tecmint_monitor.sh”脚本,只需像下面这样使用-i(安装)选项就可以了。

./tecmint_monitor.sh -i 

在提示你输入root密码时输入该密码。如果一切顺利,你会看到像下面这样的安装成功信息。

Password: 
Congratulations! Script Installed, now run monitor Command

安装完毕后,你可以在任何位置,以任何用户调用命令‘monitor’来运行该脚本。如果你不喜欢安装,你需要在每次运行时输入路径。

# ./Path/to/script/tecmint_monitor.sh

现在,以任何用户从任何地方运行monitor命令,就是这么简单:

$ monitor

TecMint Monitor Script in Action

你运行命令就会获得下面这些各种各样和系统相关的信息:

  • 互联网连通性
  • 操作系统类型
  • 操作系统名称
  • 操作系统版本
  • 架构
  • 内核版本
  • 主机名
  • 内部IP
  • 外部IP
  • 域名服务器
  • 已登录用户
  • 内存使用率
  • 交换分区使用率
  • 磁盘使用率
  • 平均负载
  • 系统开机时间

使用-v(版本)开关来检查安装的脚本的版本。

$ monitor -v

tecmint_monitor version 0.1
Designed by Tecmint.com
Released Under Apache 2.0 License

小结

该脚本在一些机器上可以开机即用,这一点我已经检查过。相信对于你而言,它也会正常工作。如果你们发现了什么毛病,可以在评论中告诉我。这个脚本还不完善,这仅仅是个开始。从这里开始,你可以将它改进到任何程度。如果你想要编辑脚本,将它带入一个更深的层次,尽管随意去做吧,别忘了给我们应的的荣誉,也别忘了把你更新后的脚本拿出来和我们分享哦,这样,我们也会更新此文来给你应得的荣誉。

别忘了和我们分享你的想法或者脚本,我们会在这儿帮助你。谢谢你们给予的所有挚爱。继续浏览,不要走开哦。


via: http://www.tecmint.com/linux-server-health-monitoring-script/

作者:Avishek Kumar 译者:GOLinux 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

在Ubuntu更新中,谁没有碰见个错误?在Ubuntu和其它基于Ubuntu的Linux发行版中,更新错误是一个共性的错误,也经常发生。这些错误出现的原因多种多样,修复起来也很简单。在本文中,我们将见到Ubuntu中各种类型频繁发生的更新错误以及它们的修复方法。

合并列表问题

当你在终端中运行更新命令时,你可能会碰到这个错误“合并列表错误”,就像下面这样:

E:Encountered a section with no Package: header,

E:Problem with MergeList /var/lib/apt/lists/archive.ubuntu.comubuntudistspreciseuniversebinary-i386Packages,

E:The package lists or status file could not be parsed or opened.’

可以使用以下命令来修复该错误:

sudo rm -r /var/lib/apt/lists/*
sudo apt-get clean && sudo apt-get update

下载仓库信息失败 -1

实际上,有两种类型的下载仓库信息失败错误。如果你的错误是这样的:

W:Failed to fetch bzip2:/var/lib/apt/lists/partial/in.archive.ubuntu.comubuntudistsoneiricrestrictedbinary-i386Packages Hash Sum mismatch,

W:Failed to fetch bzip2:/var/lib/apt/lists/partial/in.archive.ubuntu.comubuntudistsoneiricmultiversebinary-i386Packages Hash Sum mismatch,

E:Some index files failed to download. They have been ignored, or old ones used instead

那么,你可以用以下命令修复:

sudo rm -rf /var/lib/apt/lists/*
sudo apt-get update

下载仓库信息失败 -2

下载仓库信息失败的另外一种类型是由于PPA过时导致的。通常,当你运行更新管理器,并看到这样的错误时:

你可以运行sudo apt-get update来查看哪个PPA更新失败,你可以把它从源列表中删除。你可以按照这个截图指南来修复下载仓库信息失败错误

下载包文件失败错误

一个类似的错误是下载包文件失败错误,像这样:

该错误很容易修复,只需修改软件源为主服务器即可。转到“软件和更新”,在那里你可以修改下载服务器为主服务器:

部分更新错误

在终端中运行更新会出现部分更新错误

Not all updates can be installed

Run a partial upgrade, to install as many updates as possible

在终端中运行以下命令来修复该错误:

sudo apt-get install -f

加载共享库时发生错误

该错误更多是安装错误,而不是更新错误。如果尝试从源码安装程序,你可能会碰到这个错误:

error while loading shared libraries:

cannot open shared object file: No such file or directory

该错误可以通过在终端中运行以下命令来修复:

sudo /sbin/ldconfig -v

你可以在这里查找到更多详细内容加载共享库时发生错误

无法获取锁 /var/cache/apt/archives/lock

在另一个程序在使用APT时,会发生该错误。假定你正在Ubuntu软件中心安装某个东西,然后你又试着在终端中运行apt。

E: Could not get lock /var/cache/apt/archives/lock – open (11: Resource temporarily unavailable)

E: Unable to lock directory /var/cache/apt/archives/

通常,只要你把所有其它使用apt的程序关了,这个问题就会好的。但是,如果问题持续,可以使用以下命令:

sudo rm /var/lib/apt/lists/lock

如果上面的命令不起作用,可以试试这个命令:

sudo killall apt-get

关于该错误的更多信息,可以在这里找到。

GPG错误: 下列签名无法验证

在添加一个PPA时,可能会导致以下错误GPG错误: 下列签名无法验证,这通常发生在终端中运行更新时:

W: GPG error: http://repo.mate-desktop.org saucy InRelease: The following signatures couldn’t be verified because the public key is not available: NO\_PUBKEY 68980A0EA10B4DE8

我们所要做的,就是获取系统中的这个公钥,从信息中获取密钥号。在上述信息中,密钥号为68980A0EA10B4DE8。该密钥可通过以下方式使用:

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 68980A0EA10B4DE8

在添加密钥后,再次运行更新就没有问题了。

BADSIG错误

另外一个与签名相关的Ubuntu更新错误是BADSIG错误,它看起来像这样:

W: A error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: http://extras.ubuntu.com precise Release: The following signatures were invalid: BADSIG 16126D3A3E5C1192 Ubuntu Extras Archive Automatic Signing Key

W: GPG error: http://ppa.launchpad.net precise Release:

The following signatures were invalid: BADSIG 4C1CBC1B69B0E2F4 Launchpad PPA for Jonathan French W: Failed to fetch http://extras.ubuntu.com/ubuntu/dists/precise/Release

要修复该BADSIG错误,请在终端中使用以下命令:

sudo apt-get clean
cd /var/lib/apt
sudo mv lists oldlist
sudo mkdir -p lists/partial
sudo apt-get clean
sudo apt-get update

本文汇集了你可能会碰到的Ubuntu更新错误,我希望这会对你处理这些错误有所帮助。你在Ubuntu中是否也碰到过其它更新错误呢?请在下面的评论中告诉我,我会试着写个快速指南。


via: http://itsfoss.com/fix-update-errors-ubuntu-1404/

作者:Abhishek 译者:GOLinux 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

各位好,今天我们将学习一些Docker容器的基础命令。Docker 是一个开源项目,提供了一个可以打包、装载和运行任何应用的轻量级容器的开放平台。它没有语言支持、框架和打包系统的限制,从小型的家用电脑到高端服务器,在何时何地都可以运行。它可以使部署和扩展web应用程序、数据库和后端服务像搭积木一样容易,而不依赖特定技术栈或提供商。Docker适用于网络环境,它正应用于数据中心、ISP和越来越多的网络服务。

因此,这里有一些你在管理Docker容器的时候会用到的一些命令。

1. 找到Docker接口

Docker默认会创建一个名为docker0的网桥接口作为连接外部世界的基础。运行中的docker容器直接连接到网桥接口docker0。默认上,docker会分配172.17.42.1/16给docker0,它是所有运行中的容器ip地址的子网。找到Docker接口的ip地址非常简单。要找出docker0网桥接口和连接到网桥上的docker容器,我们可以在安装了docker的终端或者shell中运行ip命令。

# ip a

Docker Interface

2. 得到Docker容器的ip地址

如我们上面读到的,docker在宿主机中创建了一个叫docker0的网桥接口。在我们创建一个新的docker容器时,它自动被默认分配了一个在该子网范围内的ip地址。因此,要检测运行中的Docker容器的ip地址,我们需要进入一个正在运行的容器并用下面的命令检查ip地址。首先,我们运行一个新的容器并进入其中。如果你已经有一个正在运行的容器,你可以跳过这个步骤。

# docker run -it ubuntu

现在,我们可以运行ip a来得到容器的ip地址了。

# ip a

Docker Container IP

3. 映射暴露的端口

要映射配置在Dockerfile的暴露端口到宿主机的高位端口,我们只需用下面带上-P标志的命令。这会打开docker容器的随机端口并映射到Dockerfile中定义的端口。下面是使用-P来打开/暴露定义的端口的例子。

# docker run -itd -P httpd

Mapping Expose Port

上面的命令会映射容器的端口到 httpd 容器的 Dockerfile 中定义的80端口上。我们用下面的命令来查看正在运行的容器暴露的端口。

# docker ps

并且可以用下面的curl命令来检查。

# curl http://localhost:49153

Curl Exposed Port

4. 映射到特定的端口上

我们也可以映射暴露端口或者docker容器端口到我们指定的端口上。要实现这个,我们用-p标志来定义我们所需的端口。这里是我们的一个例子。

# docker run -itd -p 8080:80 httpd

上面的命令会映射(宿主机的)8080端口到(容器的)80上。我们可以运行curl来检查这点。

# curl http://localhost:8080

Mapping Specific Port

5. 创建自己的网桥

要给容器创建一个自定义的IP地址,在本篇中我们会创建一个名为br0的新网桥。要分配需要的ip地址,我们需要在运行docker的宿主机中运行下面的命令。

# stop docker.io
# ip link add br0 type bridge
# ip addr add 172.30.1.1/20 dev br0
# ip link set br0 up
# docker -d -b br0

Creating Bridge Interface

创建完docker网桥之后,我们要让docker的守护进程知道它。

# echo 'DOCKER_OPTS="-b=br0"' >> /etc/default/docker
# service docker.io start

Adding Interface to Docker

到这里,桥接后的接口将会分配给容器在桥接子网内的新ip地址。

6. 链接到另外一个容器上

我们可以用Docker将一个容器连接到另外一个上。我们可以在不同的容器上运行不同的程序,并且相互连接或链接。链接允许容器间相互连接并从一个容器上安全地传输信息给另一个容器。要做到这个,我们可以使用--link标志。首先,我们使用--name标志来标示training/postgres镜像。

# docker run -d --name db training/postgres

Running db Container

完成之后,我们将容器db与training/webapp链接来形成新的叫web的容器。

# docker run -d -P --name web --link db:db training/webapp python app.py

linking two containers

总结

Docker网络很神奇也好玩,我们可以对docker容器做很多事情。我们可以把玩这些简单而基础的docker网络命令。docker的网络是非常先进的,我们可以用它做很多事情。

如果你有任何的问题、建议、反馈请在下面的评论栏写下来以便于我们我们可以提升或者更新文章的内容。谢谢! 玩得开心!:-)


via: http://linoxide.com/linux-how-to/networking-commands-docker-containers/

作者:Arun Pyasi 译者:geekpi 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

问题: 我的PC上安装了旧版的Ubuntu 13.04(急切的浣熊)。当我在上面运行“sudo apt-get update”时,它丢给了我一大堆“404 Not Found”错误,结果是我不能使用apt-get或aptitude来安装或更新任何软件包了。由于该错误的原因,我甚至不能将它升级到更新的版本。我怎样才能修复这个问题啊?

每个Ubuntu版本都有生命结束周期(EOL)时间;常规的Ubuntu发行版提供18个月的支持,而LTS(长期支持)版本则长达3年(服务器版本)和5年(桌面版本)。当某个Ubuntu版本达到生命结束周期时,其仓库就不能再访问了,你也不能再从Canonical获取任何维护更新和安全补丁。在撰写本文时,Ubuntu 13.04(急切的浣熊)已经达到了它的生命结束周期。

如果你所使用的Ubuntu系统已经被结束生命周期,你就会从apt-get或aptitude得到以下404错误,因为它的仓库已经被遗弃了。

W: Failed to fetch http://us.archive.ubuntu.com/ubuntu/dists/raring-backports/multiverse/binary-i386/Packages  404  Not Found [IP: 91.189.91.13 80]

W: Failed to fetch http://extras.ubuntu.com/ubuntu/dists/raring/main/binary-amd64/Packages  404  Not Found

W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/raring-security/universe/binary-i386/Packages  404  Not Found [IP: 91.189.88.149 80]

E: Some index files failed to download. They have been ignored, or old ones used instead

对于那些还在使用旧版本Ubuntu的用户,Canonical维护了一个old-releases.ubuntu.com的网站,这里包含了结束生命周期的仓库归档。因此,当Canonical对你安装的Ubuntu版本结束支持时,你需要将仓库切换到old-releases.ubuntu.com(除非你在结束生命周期之前想要升级)。

这里,通过切换到旧版本仓库提供了一个快速修复“404 Not Found”错误的便捷方式。

首先,使用old-releases仓库替换main/security仓库,就像下面这样。

$ sudo sed -i -r 's/([a-z]{2}\.)?archive.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list
$ sudo sed -i -r 's/security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list

然后,使用文本编辑器打开/etc/apt/sources.list,并查找extras.ubuntu.com。该仓库也不再支持Ubuntu 13.04了,所以你需要使用“#”号将extras.ubuntu.com注释掉。

#deb http://extras.ubuntu.com/ubuntu raring main
#deb-src http://extras.ubuntu.com/ubuntu raring main

现在,你应该可以在旧版不受支持的Ubuntu上安装或更新软件包了。


via: http://ask.xmodulo.com/404-not-found-error-apt-get-update-ubuntu.html

作者:Dan Nanni 译者:GOLinux 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

大家好,今天我们学习一下在 Linux 上用 iptables 实现从一个网络接口到另一个接口的IP转发(数据包转发)。IP转发的概念是,使 Linux 机器像路由器一样将数据从一个网络发送到另一个网络。所以,它能作为一个路由器或者代理服务器,实现将一个连接的互联网或者网络连接共享给多个客户端机器。

这是一些启用IP转发或网络包转发方法的简单步骤。

1. 启用 IPv4 转发

首先,我们需要在我们的 Linux 操作系统上启用 IPv4 转发。要做到这点,我们需要用 sudo 模式在 shell 或终端下执行下面的命令。

$ sudo -s

# echo 1 > /proc/sys/net/ipv4/ip_forward

临时IP转发

注意:上面的命令能马上启用ip转发,但只是临时的,直到下一次重启。要永久启用,我们需要使用我们惯用的文本编辑器打开 /etc/sysctl.conf 文件。

# nano /etc/sysctl.conf

然后,增加 net.ipv4.ip\_forward = 1 到文件中,或者删除那行的注释,保存并退出文件。

net.ipv4.ip_forward = 1

编辑 Sysctl

运行下面的命令启用更改。

# sysctl -p /etc/sysctl.conf

启用 Sysctl 更改

2. 配置 Iptables 防火墙

我们需要允许特定的(或者所有的)数据包通过我们的路由器。在这之前,我们要知道连接我们 Linux 的网络设备的接口名称。我们可以通过在终端或者 shell 运行以下命令获得接口名称。

# ifconfig -a

Ifconfig

这里,在我们的机器中, eth2 是连接到互联网或者网络的网卡接口, wlan2 是我们要使用 iptables 从 eth2 转发数据包的接口。要实现转发,我们需要运行以下命令。

# iptables -A FORWARD -i wlan2 -o eth2 -j ACCEPT

注意:请用你 Linux 机器中的可用设备名称替换 wlan2 和 eth2。

现在,由于netfilter/iptables是一个无状态的防火墙,我们需要让 iptables 允许已建立的连接通过。要做到这点,我们要运行下面的命令。

# iptables -A FORWARD -i eth2 -o wlan2 -m state --state ESTABLISHED,RELATED  -j ACCEPT

用 iptables 转发

3. 配置 NAT

然后,最后我们需要通过执行下面的命令修改发送到互联网的数据包的源地址为 eth2。

# iptables -t nat -A POSTROUTING -o eth2 -j MASQUERADE

配置 nat

总结

最后,我们在我们以 iptables 作为防火墙 Linux 机器上成功的配置了从一个接口到另一个接口的数据包转发。这篇文章教给你将你的私有接口连接到互联网,不需要桥接接口,而是将从一个接口进来的数据包路由到另一个接口。就是这些,如果你有任何问题、建议、反馈,请写到下面的评论框中,然后我们可以改进或更新我们的内容。非常感谢!享受吧 :-)


via: http://linoxide.com/firewall/ip-forwarding-connecting-private-interface-internet/

作者:Arun Pyasi 译者:ictlyh 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

通常,当人们提到“shell脚本语言”时,浮现在他们脑海中是bash,ksh,sh或者其它相类似的linux/unix脚本语言。脚本语言是与计算机交流的另外一种途径。使用图形化窗口界面(不管是windows还是linux都无所谓)用户可以移动鼠标并点击各种对象,比如按钮、列表、选框等等。但这种方式在每次用户想要计算机/服务器完成相同任务时(比如说批量转换照片,或者下载新的电影、mp3等)却是十分不方便。要想让所有这些事情变得简单并且自动化,我们可以使用shell脚本。

某些编程语言,像pascal、foxpro、C、java之类,在执行前需要先进行编译。它们需要合适的编译器来让我们的代码完成某个任务。

而其它一些编程语言,像php、javascript、visualbasic之类,则不需要编译器,因此它们需要解释器,而我们不需要编译代码就可以运行程序。

shell脚本也像解释器一样,但它通常用于调用外部已编译的程序。然后,它会捕获输出结果、退出代码并根据情况进行处理。

Linux世界中最为流行的shell脚本语言之一,就是bash。而我认为(这是我自己的看法)原因在于,默认情况下bash shell可以让用户便捷地通过历史命令(先前执行过的)导航,与之相反的是,ksh则要求对.profile进行一些调整,或者记住一些“魔术”组合键来查阅历史并修正命令。

好了,我想这些介绍已经足够了,剩下来哪个环境最适合你,就留给你自己去判断吧。从现在开始,我将只讲bash及其脚本。在下面的例子中,我将使用CentOS 6.6和bash-4.1.2。请确保你有相同版本,或者更高版本。

Shell脚本流

shell脚本语言就跟和几个人聊天类似。你只需把所有命令想象成能帮你做事的那些人,只要你用正确的方式来请求他们去做。比如说,你想要写文档。首先,你需要纸。然后,你需要把内容说给某个人听,让他帮你写。最后,你想要把它存放到某个地方。或者说,你想要造一所房子,因而你需要请合适的人来清空场地。在他们说“事情干完了”,那么另外一些工程师就可以帮你来砌墙。最后,当这些工程师们也告诉你“事情干完了”的时候,你就可以叫油漆工来给房子粉饰了。如果你让油漆工在墙砌好前就来粉饰,会发生什么呢?我想,他们会开始发牢骚了。几乎所有这些像人一样的命令都会说话,如果它们完成了工作而没有发生什么问题,那么它们就会告诉“标准输出”。如果它们不能做你叫它们做的事——它们会告诉“标准错误”。这样,最后,所有的命令都通过“标准输入”来听你的话。

快速实例——当你打开linux终端并写一些文本时——你正通过“标准输入”和bash说话。那么,让我们来问问bash shell who am i(我是谁?)吧。

root@localhost ~]# who am i                                <--- 你通过标准输入对 bash shell 说
root     pts/0        2015-04-22 20:17 (192.168.1.123)     <--- bash shell通过标准输出回答你

现在,让我们说一些bash听不懂的问题:

[root@localhost ~]# blablabla           <--- 哈,你又在和标准输入说话了
-bash: blablabla: command not found     <--- bash通过标准错误在发牢骚了

“:”之前的第一个单词通常是向你发牢骚的命令。实际上,这些流中的每一个都有它们自己的索引号(LCTT 译注:文件句柄号):

  • 标准输入(stdin) - 0
  • 标准输出(stdout) - 1
  • 标准错误(stderr) - 2

如果你真的想要知道哪个输出命令说了些什么——你需要将那次发言重定向到(在命令后使用大于号“>”和流索引)文件:

[root@localhost ~]# blablabla 1> output.txt
-bash: blablabla: command not found

在本例中,我们试着重定向流1(stdout)到名为output.txt的文件。让我们来看对该文件内容所做的事情吧,使用cat命令可以做这事:

[root@localhost ~]# cat output.txt
[root@localhost ~]#

看起来似乎是空的。好吧,现在让我们来重定向流2(stderr):

[root@localhost ~]# blablabla 2> error.txt
[root@localhost ~]#

好吧,我们看到牢骚话没了。让我们检查一下那个文件:

[root@localhost ~]# cat error.txt
-bash: blablabla: command not found
[root@localhost ~]#

果然如此!我们看到,所有牢骚话都被记录到errors.txt文件里头去了。

有时候,命令会同时产生stdoutstderr。要重定向它们到不同的文件,我们可以使用以下语句:

command 1>out.txt 2>err.txt

要缩短一点语句,我们可以忽略“1”,因为默认情况下stdout会被重定向:

command >out.txt 2>err.txt

好吧,让我们试试做些“坏事”。让我们用rm命令把file1和folder1给删了吧:

[root@localhost ~]# rm -vf folder1 file1 > out.txt 2>err.txt

现在来检查以下输出文件:

[root@localhost ~]# cat out.txt
removed `file1'
[root@localhost ~]# cat err.txt
rm: cannot remove `folder1': Is a directory
[root@localhost ~]#

正如我们所看到的,不同的流被分离到了不同的文件。有时候,这也不是很方便,因为我们想要查看出现错误时,在某些操作前面或后面所连续发生的事情。要实现这一目的,我们可以重定向两个流到同一个文件:

command >>out_err.txt 2>>out_err.txt

注意:请注意,我使用“>>”替代了“>”。它允许我们附加到文件,而不是覆盖文件。

我们也可以重定向一个流到另一个:

command >out_err.txt 2>&1

让我来解释一下吧。所有命令的标准输出将被重定向到out\_err.txt,错误输出将被重定向到流1(上面已经解释过了),而该流会被重定向到同一个文件。让我们看这个实例:

[root@localhost ~]# rm -fv folder2 file2 >out_err.txt 2>&1
[root@localhost ~]# cat out_err.txt
rm: cannot remove `folder2': Is a directory
removed `file2'
[root@localhost ~]#

看着这些组合的输出,我们可以将其说明为:首先,rm命令试着将folder2删除,而它不会成功,因为linux要求-r键来允许rm命令删除文件夹,而第二个file2会被删除。通过为rm提供-v(详情)键,我们让rm命令告诉我们每个被删除的文件或文件夹。

这些就是你需要知道的,关于重定向的几乎所有内容了。我是说几乎,因为还有一个更为重要的重定向工具,它称之为“管道”。通过使用|(管道)符号,我们通常重定向stdout流。

比如说,我们有这样一个文本文件:

[root@localhost ~]# cat text_file.txt
This line does not contain H e l l o  word
This lilne contains Hello
This also containd Hello
This one no due to HELLO all capital
Hello bash world!

而我们需要找到其中某些带有“Hello”的行,Linux中有个grep命令可以完成该工作:

[root@localhost ~]# grep Hello text_file.txt
This lilne contains Hello
This also containd Hello
Hello bash world!
[root@localhost ~]#

当我们有个文件,想要在里头搜索的时候,这用起来很不错。当如果我们需要在另一个命令的输出中查找某些东西,这又该怎么办呢?是的,当然,我们可以重定向输出到文件,然后再在文件里头查找:

[root@localhost ~]# fdisk -l>fdisk.out
[root@localhost ~]# grep "Disk /dev" fdisk.out
Disk /dev/sda: 8589 MB, 8589934592 bytes
Disk /dev/mapper/VolGroup-lv_root: 7205 MB, 7205814272 bytes
Disk /dev/mapper/VolGroup-lv_swap: 855 MB, 855638016 bytes
[root@localhost ~]#

如果你打算grep一些双引号引起来带有空格的内容呢!

注意:fdisk命令显示关于Linux操作系统磁盘驱动器的信息。

就像我们看到的,这种方式很不方便,因为我们不一会儿就把临时文件空间给搞乱了。要完成该任务,我们可以使用管道。它们允许我们重定向一个命令的stdout到另一个命令的stdin流:

[root@localhost ~]# fdisk -l | grep "Disk /dev"
Disk /dev/sda: 8589 MB, 8589934592 bytes
Disk /dev/mapper/VolGroup-lv_root: 7205 MB, 7205814272 bytes
Disk /dev/mapper/VolGroup-lv_swap: 855 MB, 855638016 bytes
[root@localhost ~]#

如你所见,我们不需要任何临时文件就获得了相同的结果。我们把fdisk stdout重定向到了grep stdin

注意 : 管道重定向总是从左至右的。

还有几个其它重定向,但是我们将把它们放在后面讲。

在shell中显示自定义信息

正如我们所知道的,通常,与shell的交流以及shell内的交流是以对话的方式进行的。因此,让我们创建一些真正的脚本吧,这些脚本也会和我们讲话。这会让你学到一些简单的命令,并对脚本的概念有一个更好的理解。

假设我们是某个公司的总服务台经理,我们想要创建某个shell脚本来注册呼叫信息:电话号码、用户名以及问题的简要描述。我们打算把这些信息存储到普通文本文件data.txt中,以便今后统计。脚本它自己就是以对话的方式工作,这会让总服务台的工作人员的小日子过得轻松点。那么,首先我们需要显示提问。对于显示信息,我们可以用echo和printf命令。这两个都是用来显示信息的,但是printf更为强大,因为我们可以通过它很好地格式化输出,我们可以让它右对齐、左对齐或者为信息留出专门的空间。让我们从一个简单的例子开始吧。要创建文件,请使用你惯用的文本编辑器(kate,nano,vi,……),然后创建名为note.sh的文件,里面写入这些命令:

echo "Phone number ?"

如何运行/执行脚本?

在保存文件后,我们可以使用bash命令来运行,把我们的文件作为它的参数:

[root@localhost ~]# bash note.sh
Phone number ?

实际上,这样来执行脚本是很不方便的。如果不使用bash命令作为前缀来执行,会更舒服一些。要让脚本可执行,我们可以使用chmod命令:

[root@localhost ~]# ls -la note.sh
-rw-r--r--. 1 root root 22 Apr 23 20:52 note.sh
[root@localhost ~]# chmod +x note.sh
[root@localhost ~]# ls -la note.sh
-rwxr-xr-x. 1 root root 22 Apr 23 20:52 note.sh
[root@localhost ~]#

注意 : ls命令显示了当前文件夹内的文件。通过添加-la键,它会显示更多文件信息。

如我们所见,在chmod命令执行前,脚本只有读(r)和写(w)权限。在执行chmod +x后,它就获得了执行(x)权限。(关于权限的更多细节,我会在下一篇文章中讲述。)现在,我们只需这么来运行:

[root@localhost ~]# ./note.sh
Phone number ?

在脚本名前,我添加了 ./ 组合。.(点)在unix世界中意味着当前位置(当前文件夹),/(斜线)是文件夹分隔符。(在Windows系统中,我们使用反斜线 \ 表示同样功能)所以,这整个组合的意思是说:“从当前文件夹执行note.sh脚本”。我想,如果我用完整路径来运行这个脚本的话,你会更加清楚一些:

[root@localhost ~]# /root/note.sh
Phone number ?
[root@localhost ~]#

它也能工作。

如果所有linux用户都有相同的默认shell,那就万事OK。如果我们只是执行该脚本,默认的用户shell就会用于解析脚本内容并运行命令。不同的shell的语法、内部命令等等有着一丁点不同,所以,为了保证我们的脚本会使用bash,我们应该添加#!/bin/bash到文件首行。这样,默认的用户shell将调用/bin/bash,而只有在那时候,脚本中的命令才会被执行:

[root@localhost ~]# cat note.sh
#!/bin/bash
echo "Phone number ?"

直到现在,我们才100%确信bash会用来解析我们的脚本内容。让我们继续。

读取输入

在显示信息后,脚本会等待用户回答。有个read命令用来接收用户的回答:

#!/bin/bash
echo "Phone number ?"
read phone

在执行后,脚本会等待用户输入,直到用户按[ENTER]键结束输入:

[root@localhost ~]# ./note.sh
Phone number ?
12345                               <--- 这儿是我输入的内容
[root@localhost ~]#

你输入的所有东西都会被存储到变量phone中,要显示变量的值,我们同样可以使用echo命令:

[root@localhost ~]# cat note.sh
#!/bin/bash
echo "Phone number ?"
read phone
echo "You have entered $phone as a phone number"
[root@localhost ~]# ./note.sh
Phone number ?
123456
You have entered 123456 as a phone number
[root@localhost ~]#

bash shell中,一般我们使用**$**(美元)符号来表明这是一个变量,除了读入到变量和其它为数不多的时候才不用这个$(将在今后说明)。

好了,现在我们准备添加剩下的问题了:

#!/bin/bash
echo "Phone number?"
read phone
echo "Name?"
read name
echo "Issue?"
read issue
[root@localhost ~]# ./note.sh
Phone number?
123
Name?
Jim
Issue?
script is not working.
[root@localhost ~]#

使用流重定向

太完美了!剩下来就是重定向所有东西到文件data.txt了。作为字段分隔符,我们将使用/(斜线)符号。

注意 : 你可以选择任何你认为是最好的分隔符,但是确保文件内容不会包含这些符号在内,否则它会导致在文本行中产生额外字段。

别忘了使用“>>”来代替“>”,因为我们想要将输出内容附加到文件末!

[root@localhost ~]# tail -2 note.sh
read issue
echo "$phone/$name/$issue">>data.txt
[root@localhost ~]# ./note.sh
Phone number?
987
Name?
Jimmy
Issue?
Keybord issue.
[root@localhost ~]# cat data.txt
987/Jimmy/Keybord issue.
[root@localhost ~]#

注意tail命令显示了文件的最后的n行。

搞定。让我们再来运行一次看看:

[root@localhost ~]# ./note.sh
Phone number?
556
Name?
Janine
Issue?
Mouse was broken.
[root@localhost ~]# cat data.txt
987/Jimmy/Keybord issue.
556/Janine/Mouse was broken.
[root@localhost ~]#

我们的文件在增长,让我们在每行前面加个日期吧,这对于今后摆弄这些统计数据时会很有用。要实现这功能,我们可以使用date命令,并指定某种格式,因为我不喜欢默认格式:

[root@localhost ~]# date
Thu Apr 23 21:33:14 EEST 2015                     <---- date命令的默认输出
[root@localhost ~]# date "+%Y.%m.%d %H:%M:%S"
2015.04.23 21:33:18                               <---- 格式化后的输出

有几种方式可以读取命令的输出到变量,在这种简单的情况下,我们将使用`(是反引号,不是单引号,和波浪号~在同一个键位):

[root@localhost ~]# cat note.sh
#!/bin/bash
now=`date "+%Y.%m.%d %H:%M:%S"`
echo "Phone number?"
read phone
echo "Name?"
read name
echo "Issue?"
read issue
echo "$now/$phone/$name/$issue">>data.txt
[root@localhost ~]# ./note.sh
Phone number?
123
Name?
Jim
Issue?
Script hanging.
[root@localhost ~]# cat data.txt
2015.04.23 21:38:56/123/Jim/Script hanging.
[root@localhost ~]#

嗯…… 我们的脚本看起来有点丑啊,让我们来美化一下。如果你要手动读取read命令,你会发现read命令也可以显示一些信息。要实现该功能,我们应该使用-p键加上信息:

[root@localhost ~]# cat note.sh
#!/bin/bash
now=`date "+%Y.%m.%d %H:%M:%S"`
read -p "Phone number: " phone
read -p "Name: " name
read -p "Issue: " issue
echo "$now/$phone/$name/$issue">>data.txt

你可以直接从控制台查找到各个命令的大量有趣的信息,只需输入:man read, man echo, man date, man ……

同意吗?它看上去是舒服多了!

[root@localhost ~]# ./note.sh
Phone number: 321
Name: Susane
Issue: Mouse was stolen
[root@localhost ~]# cat data.txt
2015.04.23 21:38:56/123/Jim/Script hanging.
2015.04.23 21:43:50/321/Susane/Mouse was stolen
[root@localhost ~]#

光标在消息的后面(不是在新的一行中),这有点意思。(LCTT 译注:如果用 echo 命令输出显示的话,可以用 -n 参数来避免换行。)

循环

是时候来改进我们的脚本了。如果用户一整天都在接电话,如果每次都要去运行,这岂不是很麻烦?让我们让这些活动都永无止境地循环去吧:

[root@localhost ~]# cat note.sh
#!/bin/bash
while true
do
        read -p "Phone number: " phone
        now=`date "+%Y.%m.%d %H:%M:%S"`
        read -p "Name: " name
        read -p "Issue: " issue
        echo "$now/$phone/$name/$issue">>data.txt
done

我已经交换了read phonenow=date行的位置。这是因为我想要在输入电话号码后再获得时间。如果我把它放在循环的首行,那么循环一次后,变量 now 就会在数据存储到文件中后马上获得时间。而这并不好,因为下一次呼叫可能在20分钟后,甚至更晚。

[root@localhost ~]# ./note.sh
Phone number: 123
Name: Jim
Issue: Script still not works.
Phone number: 777
Name: Daniel
Issue: I broke my monitor
Phone number: ^C
[root@localhost ~]# cat data.txt
2015.04.23 21:38:56/123/Jim/Script hanging.
2015.04.23 21:43:50/321/Susane/Mouse was stolen
2015.04.23 21:47:55/123/Jim/Script still not works.
2015.04.23 21:48:16/777/Daniel/I broke my monitor
[root@localhost ~]#

注意: 要从无限循环中退出,你可以按[Ctrl]+[C]键。Shell会显示^表示 CTRL 键

使用管道重定向

让我们添加更多功能到我们的“弗兰肯斯坦(Frankenstein)”,我想要脚本在每次呼叫后显示某个统计数据。比如说,我想要查看各个号码呼叫了我几次。对于这个,我们应该cat文件data.txt:

[root@localhost ~]# cat data.txt
2015.04.23 21:38:56/123/Jim/Script hanging.
2015.04.23 21:43:50/321/Susane/Mouse was stolen
2015.04.23 21:47:55/123/Jim/Script still not works.
2015.04.23 21:48:16/777/Daniel/I broke my monitor
2015.04.23 22:02:14/123/Jimmy/New script also not working!!!
[root@localhost ~]#

现在,所有输出我们都可以重定向到cut命令,让cut来把每行切成一块一块(我们使用分隔符“/”),然后打印第二个字段:

[root@localhost ~]# cat data.txt | cut -d"/" -f2
123
321
123
777
123
[root@localhost ~]#

现在,我们可以把这个输出重定向打另外一个命令sort

[root@localhost ~]# cat data.txt | cut -d"/" -f2|sort
123
123
123
321
777
[root@localhost ~]#

然后只留下唯一的行。要统计唯一条目,只需添加-c键到uniq命令:

[root@localhost ~]# cat data.txt | cut -d"/" -f2 | sort | uniq -c
    3 123
    1 321
    1 777
[root@localhost ~]#

只要把这个添加到我们的循环的最后:

#!/bin/bash
while true
do
        read -p "Phone number: " phone
        now=`date "+%Y.%m.%d %H:%M:%S"`
        read -p "Name: " name
        read -p "Issue: " issue
        echo "$now/$phone/$name/$issue">>data.txt
        echo "===== We got calls from ====="
        cat data.txt | cut -d"/" -f2 | sort | uniq -c
        echo "--------------------------------"
done

运行:

[root@localhost ~]# ./note.sh
Phone number: 454
Name: Malini
Issue: Windows license expired.
===== We got calls from =====
    3 123
    1 321
    1 454
    1 777
--------------------------------
Phone number: ^C

当前场景贯穿了几个熟知的步骤:

  • 显示消息
  • 获取用户输入
  • 存储值到文件
  • 处理存储的数据

但是,如果用户有点责任心,他有时候需要输入数据,有时候需要统计,或者可能要在存储的数据中查找一些东西呢?对于这些事情,我们需要使用switches/cases,并知道怎样来很好地格式化输出。这对于在shell中“画”表格的时候很有用。


via: http://linoxide.com/linux-shell-script/guide-start-learning-shell-scripting-scratch/

作者:Petras Liumparas 译者:GOLinux 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出