分类归档: Linux

你可能不知道的Shell | 酷壳 – CoolShell.cn

via 你可能不知道的Shell | 酷壳 – CoolShell.cn.

 

Shell也叫做命令行界面,它是*nix操作系统下用户和计算机的交互界面。Shell这个词是指操作系统中提供访问内核服务的程序。

这篇文章向大家介绍Shell一些非广为人知、但却实用有趣的知识,权当品尝shell主食后的甜点吧。

先科普几个你可能不知道的事实:

  1. Shell几乎是和Unix操作系统一起诞生,第一个Unix Shell是肯·汤普逊(Ken Thompson)以Multics上的Shell为模范在1971年改写而成,并命名Thompson sh。即便是后来流行的bash(shell的一种变体),它的年龄实际上比当前流行的所有的Linux kernel都大,可谓在Linux系统上是先有Shell再有Kernel。
  2. 当前绝大部分*nix和MacOS操作系统里的默认的Shell都是bash,bash由Brian Fox在1987年创造,全称Bourne Again shell ( bash)。
  3. 你或许听说除了bash之外,还有Bourne shell ( sh),Korn shell ( ksh),C shell (包括 csh and tcsh),但是你知道这个星球上一共存在着大约50多种不同的shell么?想了解他们,请参考 http://www.freebsd.org/ports/shells.html
  4. 每个月tiobe上都会给一个编程语言的排名,来显示各种语言的流行度。排名指数综合了全球范围内使用该语言的工程师人数、教学的课程数和第三方供应商数。截止至2012年11月份,tiobe公布的编程语言排行榜里,bash的指数是0.56%排名22位。如果算上它旗下的awk 0.21%和tcl 0.146%,大概就能排到14名。注意这里还不包括bash的同源的兄弟姐妹csh、ksh等,算上它们,shell家族有望接近前十。值得一提的是一直以来shell的排名就很稳定,不像某些“暴发户”语言,比如objective-c,这些语言的流行完全是因为当前Apple系的崛起,但这种热潮极有可能来得快去得更快。
  5. Tiobe下全球编程语言排名1~20
    Tiobe下全球编程语言排名21~40
  6. 全球最大的源代码仓库Github里,shell相关的项目数占到了8%,跻身前5和Java相当,可见在实战工程里,shell可谓宝刀不老。图片来源,参见这里
    Github里源代码项目按编程语言排名

再分享一些可能你不知道的shell用法和脚本,简单&强大!

在阅读以下部分前,强烈建议读者打开一个shell实验,这些都不是shell教科书里的大路货哦:)

  1. !$
    !$是一个特殊的环境变量,它代表了上一个命令的最后一个字符串。如:你可能会这样:
    $mkdir mydir
    $mv mydir yourdir

    $cd yourdir
    可以改成:
    $mkdir mydir
    $mv !$ yourdir
    $cd !$
  2. sudo !!
    以root的身份执行上一条命令 。
    场景举例:比如Ubuntu里用apt-get安装软件包的时候是需要root身份的,我们经常会忘记在apt-get前加sudo。每次不得不加上sudo再重新键入这行命令,这时可以很方便的用sudo !!完事。
    (陈皓注:在shell下,有时候你会输入很长的命令,你可以使用!xxx来重复最近的一次命令,比如,你以前输入过,vi /where/the/file/is, 下次你可以使用 !vi 重得上次最近一次的vi命令。)
  3. cd –
    回到上一次的目录 。
    场景举例:当前目录为/home/a,用cd ../b切换到/home/b。这时可以通过反复执行cd –命令在/home/a/home/b之间来回方便的切换。
    (陈皓注:cd ~ 是回到自己的Home目录,cd ~user,是进入某个用户的Home目录)
  4. ‘ALT+.’ or ‘<ESC> .’
    热建alt+. 或 esc+. 可以把上次命令行的参数给重复出来。
  5. ^old^new
    替换前一条命令里的部分字符串。
    场景:echo "wanderful",其实是想输出echo "wonderful"。只需要^a^o就行了,对很长的命令的错误拼写有很大的帮助。(陈皓注:也可以使用 !!:gs/old/new
  6. du -s * | sort -n | tail
    列出当前目录里最大的10个文件。
  7. :w !sudo tee %
    在vi中保存一个只有root可以写的文件
  8. date -d@1234567890
    时间截转时间
  9. > file.txt
    创建一个空文件,比touch短。
  10. mtr coolshell.cn
    mtr命令比traceroute要好。
  11. 在命令行前加空格,该命令不会进入history里。
  12. echo “ls -l” | at midnight
    在某个时间运行某个命令。
  13. curl -u user:pass -d status=”Tweeting from the shell” http://twitter.com/statuses/update.xml
    命令行的方式更新twitter。
  14. curl -u username –silent “https://mail.google.com/mail/feed/atom” | perl -ne ‘print “\t” if /<name>/; print “$2\n” if /<(title|name)>(.*)<\/\1>/;’
    检查你的gmail未读邮件
  15. ps aux | sort -nk +4 | tail
    列出头十个最耗内存的进程
  16. man ascii
    显示ascii码表。
    场景:忘记ascii码表的时候还需要google么?尤其在天朝网络如此“顺畅”的情况下,就更麻烦在GWF多应用一次规则了,直接用本地的man ascii吧。
  17. ctrl-x e
    快速启动你的默认编辑器(由变量$EDITOR设置)。
  18. netstat –tlnp
    列出本机进程监听的端口号。(陈皓注:netstat -anop 可以显示侦听在这个端口号的进程)
  19. tail -f /path/to/file.log | sed '/^Finished: SUCCESS$/ q'
    当file.log里出现Finished: SUCCESS时候就退出tail,这个命令用于实时监控并过滤log是否出现了某条记录。
  20. ssh user@server bash < /path/to/local/script.sh
    在远程机器上运行一段脚本。这条命令最大的好处就是不用把脚本拷到远程机器上。
  21. ssh user@host cat /path/to/remotefile | diff /path/to/localfile –
    比较一个远程文件和一个本地文件
  22. net rpc shutdown -I ipAddressOfWindowsPC -U username%password
    远程关闭一台Windows的机器
  23. screen -d -m -S some_name ping my_router
    后台运行一段不终止的程序,并可以随时查看它的状态。-d -m参数启动“分离”模式,-S指定了一个session的标识。可以通过-R命令来重新“挂载”一个标识的session。更多细节请参考screen用法 man screen
  24. wget --random-wait -r -p -e robots=off -U mozilla http://www.example.com
    下载整个www.example.com网站。(注:别太过分,大部分网站都有防爬功能了:))
  25. curl ifconfig.me
    当你的机器在内网的时候,可以通过这个命令查看外网的IP。
  26. convert input.png -gravity NorthWest -background transparent -extent 720×200  output.png
    改一下图片的大小尺寸
  27. lsof –i
    实时查看本机网络服务的活动状态。
  28. vim scp://username@host//path/to/somefile
    vim一个远程文件
  29. python -m SimpleHTTPServer
    一句话实现一个HTTP服务,把当前目录设为HTTP服务目录,可以通过http://localhost:8000访问 这也许是这个星球上最简单的HTTP服务器的实现了。
  30. history | awk '{CMD[$2]++;count++;} END { for (a in CMD )print CMD[a] " " CMD[a]/count*100 "% " a }' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl | head -n10
    (陈皓注:有点复杂了,history|awk ‘{print $2}’|awk ‘BEGIN {FS=”|”} {print $1}’|sort|uniq -c|sort -rn|head -10)
    这行脚本能输出你最常用的十条命令,由此甚至可以洞察你是一个什么类型的程序员。
  31. tr -c “[:digit:]” ” ” < /dev/urandom | dd cbs=$COLUMNS conv=unblock | GREP_COLOR=”1;32″ grep –color “[^ ]”
    想看看Marix的屏幕效果吗?(不是很像,但也很Cool!)

看不懂行代码?没关系,系统的学习一下*nix shell脚本吧,力荐《Linux命令行与Shell脚本编程大全》

最后还是那句Shell的至理名言:(陈皓注:下面的那个马克杯很不错啊,404null.com挺有意思的)

“Where there is a shell,there is a way!”

Shell至理名言
Unix Shell变色马克杯

Unix Shell变色马克杯


参考文献:

Read: 467

怎么打开/查看MySQL的SQL记录?

我们在开发的时候经常经常遇到在程序中增加调试语句很麻烦的情况,这时候难免会想如果Mysql能看到我们执行了什么SQL语句就好了。

实际上MySQL是有这个功能的,在MySQL 5.1的时候我们就可以通过mysqld的-l参数来启动mysql来记录查询日志。

但是现在-l参数自5.1.12之后已经不推荐使用了,改为–general_log

As of MySQL 5.1.12, as an alternative to –log or -l, use –general_log[={0|1}] to specify the initial general query log state. In this case, the default general query log file name is used. With no argument or an argument of 1, –general_log enables the log. With an argument of 0, this option disables the log.

 

–general_log 打开后日志默认会输出到你的data目录下,默认文件名是 hostname.log。hostname是你的机器名,在windows上貌似是localhost或者其他什么奇怪的名字吧。

 

如果服务器已经启动或者不是直接用mysqld启动服务器的怎么办呢?(运行时开关General Log)

执行下面两个SQL就可以开关日志

打开日志

SET GLOBAL general_log = 'ON';

关闭日志

SET GLOBAL general_log = 'OFF';

 

配置文件可以设置General Log的开关吗?

在配置文件中[mysqld]段增加下面一行

general_log = 1

如果需要指定日志文件路径

general_log_file = 文件路径

Read: 6917

合并多个Redis dump.rdb 到一个rdb的多个database

公司的服务器上运行了多个redis,现在希望合并到一个redis,用上redis的多database特性。

在网上找了一圈发现没有比较好的工具可以进行这个处理。

看过一个redis-dump号称可以导出json再进行导入,结果alpha版本的程序真心不靠谱,运行后报错:

undefined method `select!` for ["x"]:Array

后来没办法只好自己研究起了RDB。其实RDB也很简单,在redis的官方网站上有完整说明

https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format

 

通过文档了解到像我正在用的这种单db的rdb,其数据结构很简单,前9个字节都是REDIS的MAGIC STRING和RDB版本号,其后跟着两个字节 FE 00 表示0号db,同理如果是1号db应该是 FE 01,再之后就是这个db内所有数据,在文件最后以一个字节FF表示文件结束。

由此得出要合并3个db只需要把每个文件去掉开头9个字节和最后一个字节,然后把对应的db index改为需要合并到的db index,最后合并到一个文件,并在开头加上之前去掉的9个字节,再在末尾加上一个FF即可完成合并。

合并完成后别忘了用 redis-check-dump检查一下

 

linux下可以用bvi进行二进制编辑,用法与vi基本一致。另外在编辑前可能需要执行一下 :set memmove 命令。

如果不想安装bvi,只用dd命令也是可以做到的,只是处理大文件的时候可能会比较慢(因为block size=1byte了,我不知道怎么在bs设的较大的情况下精确控制skip和count)。

 

dd bs=1 if=dump.rdb of=out.rdb skip=9 count={你的dump.rdb的文件大小-10,单位字节}

 

这样执行后生成的out.rdb只包含从FEXX开始的数据信息。如果要合并到一个库,可以去掉第一个文件的EOF标识,去掉第二个文件的前9个字节。再cat到一个文件即可。

 

注意:导出前记得用redis-cli save并关闭服务以保证数据一致

Read: 623

CentOS 6.2(RHEL6)高可用服务器环境搭建

目标

为四台新服务器部署MySQL、Coreseek、Redis、MongoDB、NFS、Varnish等服务。四台服务器分为两组、使用Linux-HA + Pacemaker 实现高可用(HA, High-Availablility)。

生产服务器基于安全原因考虑不会安装gcc等编译工具,因此所有软件的编译工作都在专门的编译机上完成。编译完成后再部署到生产环境,依赖用yum、rpm解决。

 

硬件环境

1-DELL R610 6核*2,Xeon5690(3.46G) 8G*6 2.5寸SAS10000转300G*2
2-DELL R610 4核*2,Xeon5620(2.13G) 8G*6 2.5寸SAS10000转300G*2
3-DELL R610 4核*2,Xeon5620(2.13G) 8G*6 2.5寸SAS10000转300G*2
4-DELL R610 6核*2,Xeon5690(3.46G) 8G*6 2.5寸SAS10000转300G*2
MD3200 3.5寸SAS15000转300G*12

 

Linux-HA  3.0

Linux-HA  3.0由三部分组成:Heartbeat、Cluster Glue、Resource Agents

  1. hearbeat本身是整个集群的基础(cluster messaging layer),负责维护集群各节点的信息以及它们之前通信;
  2. cluster-glue相当于一个中间层,可以将heartbeat和crm(pacemaker)联系起来,主要包含2个部分,LRM和STONITH;
  3. resource-agent,就是各种的资源的ocf脚本,这些脚本将被LRM调用从而实现各种资源启动、停止、监控等等。
Note
直到 Heartbeat release 2.1.3 , PacemakerHeartbeat 都是 Linux-HA 保护伞项目的一部分. 在3.0版后, Pacemaker 被作为一个独立项目分割了出去, 且依然完全支持 Heartbeat .

Pacemaker

Pacemaker = CRM = Cluster Resource Manager,

 

通过yum安装

 

yum install cluster-glue resource-agents pacemaker

Installed:
cluster-glue.x86_64 0:1.0.5-2.el6 pacemaker.x86_64 0:1.1.6-3.el6 resource-agents.x86_64 0:3.9.2-7.el6

Dependency Installed:
cifs-utils.x86_64 0:4.8.1-5.el6 cluster-glue-libs.x86_64 0:1.0.5-2.el6 clusterlib.x86_64 0:3.0.12.1-23.el6_2.1
corosync.x86_64 0:1.4.1-4.el6_2.2 corosynclib.x86_64 0:1.4.1-4.el6_2.2 gnutls.x86_64 0:2.8.5-4.el6_2.2
keyutils.x86_64 0:1.4-3.el6 libevent.x86_64 0:1.4.13-1.el6 libgssglue.x86_64 0:0.1-11.el6
libibverbs.x86_64 0:1.1.5-3.el6 libnl.x86_64 0:1.1-14.el6 librdmacm.x86_64 0:1.0.14.1-3.el6
libtalloc.x86_64 0:2.0.1-1.1.el6 libtasn1.x86_64 0:2.3-3.el6_2.1 libtirpc.x86_64 0:0.2.1-5.el6
lm_sensors-libs.x86_64 0:3.1.1-10.el6 net-snmp-libs.x86_64 1:5.5-37.el6_2.1 nfs-utils.x86_64 1:1.2.3-15.el6_2.1
nfs-utils-lib.x86_64 0:1.1.5-4.el6 pacemaker-cli.x86_64 0:1.1.6-3.el6 pacemaker-cluster-libs.x86_64 0:1.1.6-3.el6
pacemaker-libs.x86_64 0:1.1.6-3.el6 perl-TimeDate.noarch 1:1.16-11.1.el6 quota.x86_64 1:3.17-16.el6
rpcbind.x86_64 0:0.2.0-8.el6 tcp_wrappers.x86_64 0:7.6-57.el6

 

环境配置

echo /opt/servers/lib > /etc/ld.so.conf.d/servers.conf
groupadd -g 1000 mysql; useradd -g mysql -u 1000 mysql
groupadd -g 3000 wwwroot; useradd -g wwwroot -u 3000 wwwroot

编译环境

CentOS 6.2 x86_64 Linux 2.6.32-220.el6.x86_64

编译环境要求

  • wget
  • mercurial
  • gcc
  • autoconf
  • automake
  • make
  • libtool
  • pkgconfig
  • glib2-devel
  • libxml2 libxml2-devel
  • bzip2-devel
  • libtool-ltdl-devel
  • libuuid-devel
  • libxslt-devel

 

编译glue

./autogen.sh
./configure --prefix=/opt/servers
make
make install

./configure 的输出

cluster-glue configuration:
  Version                  = 1.0.9 (Build: 0a08a469fdc8a0db1875369497bc83c0523ceb21)
  Features                 =

  Prefix                   = /opt/servers
  Executables              = /opt/servers/sbin
  Man pages                = /opt/servers/share/man
  Libraries                = /opt/servers/lib
  Header files             = /opt/servers/include
  Arch-independent files   = /opt/servers/share
  Documentation            = /opt/servers/share/doc/cluster-glue
  State information        = /opt/servers/var
  System configuration     = /opt/servers/etc

  Use system LTDL          = yes

  HA group name            = haclient
  HA user name             = hacluster

  CFLAGS                   = -g -O2 -ggdb3 -O0  -fgnu89-inline -fstack-protector-all -Wall -Waggregate-return -Wbad-function-cast -Wcast-qual -Wcast-align -Wdeclaration-after-statement -Wendif-labels -Wfloat-equal -Wformat=2 -Wformat-security -Wformat-nonliteral -Winline -Wmissing-prototypes -Wmissing-declarations -Wmissing-format-attribute -Wnested-externs -Wno-long-long -Wno-strict-aliasing -Wpointer-arith -Wstrict-prototypes -Wwrite-strings -ansi -D_GNU_SOURCE -DANSI_ONLY -Werror
  Libraries                = -lbz2 -lxml2 -lc -luuid -lrt -ldl  -lglib-2.0   -lltdl
  Stack Libraries          =

注意 Use system LTDL, 在未安装lib tool-ltdl-devel之前该项会显示为no,且编译会报错

Making all in libltdl
gmake[1]: Entering directory `/root/build/Reusable-Cluster-Components-glue--glue-1.0.9/libltdl'
gmake[1]: *** 没有规则可以创建目标“all”。 停止。
gmake[1]: Leaving directory `/root/build/Reusable-Cluster-Components-glue--glue-1.0.9/libltdl'
make: *** [all-recursive] 错误 1

另外一个可能遇到的错误是关于uuid的,简单的yum install libuuid-devel可以解决,在解决这个问题的时候我google了一下,如果不行可能还会需要在configure的时候带上参数 LIBS=’/lib/libuuid.so.1’(注意,如果是64位则是/lib64/….)。

./.libs/libplumb.so: undefined reference to `uuid_parse'
./.libs/libplumb.so: undefined reference to `uuid_generate'
./.libs/libplumb.so: undefined reference to `uuid_copy'
./.libs/libplumb.so: undefined reference to `uuid_is_null'
./.libs/libplumb.so: undefined reference to `uuid_unparse'
./.libs/libplumb.so: undefined reference to `uuid_clear'
./.libs/libplumb.so: undefined reference to `uuid_compare'
collect2: ld returned 1 exit status
gmake[2]: *** [ipctest] 错误 1
gmake[2]: Leaving directory `/root/build/Reusable-Cluster-Components-glue--glue-1.0.9/lib/clplumbing'
gmake[1]: *** [all-recursive] 错误 1
gmake[1]: Leaving directory `/root/build/Reusable-Cluster-Components-glue--glue-1.0.9/lib'
make: *** [all-recursive] 错误 1

编译Heartbeat 3

./bootstrap
LIBS="/lib64/libuuid.so.1" CFLAGS="-I/opt/servers/include -L/opt/servers/lib" ./configure --prefix=/opt/servers
make
make install

因为我们的安装路径非标准路径,所以这里要指定一下CFLAGS 的include path,否则configure会因找不到glue_config.h而报错

checking for heartbeat/glue_config.h... no
configure: error: in `/root/build/Heartbeat-3-0-7e3a82377fa8':
configure: error: Core development headers were not found
See `config.log' for more details.

虽然已经在ldconfig 里加上了/opt/servers/lib 但是如果不在CFLAGS里指定 lib path 还是会导致 ld 找不到 -lplumb (/opt/servers/lib/libplumb.so)。
另外如果没有安装libxslt-devel在make的时候还会导致 “gmake[1]: –xinclude: Command not found”。

./configure 输出

heartbeat configuration:
  Version                  = "3.0.5"
  Executables              = "/opt/servers/sbin"
  Man pages                = "/opt/servers/share/man"
  Libraries                = "/opt/servers/lib64"
  Header files             = "/opt/servers/include"
  Arch-independent files   = "/opt/servers/share"
  Documentation files      = "/opt/servers/share/doc/heartbeat"
  State information        = "/opt/servers/var"
  System configuration     = "/opt/servers/etc"
  Init (rc) scripts        = "/etc/init.d"
  Init (rc) defaults       = "/etc/sysconfig"
  Use system LTDL          = "yes"
  HA group name            = "haclient"
  HA group id              = "65"
  HA user name             = "hacluster"
  HA user user id          = "17"
  Build dopd plugin        = "yes"
  Enable times kludge      = "yes"
  CC_WARNINGS              = " -Wall -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wdeclaration-after-statement -Wpointer-arith -Wwrite-strings -Wcast-qual -Wcast-align -Wbad-function-cast -Winline -Wmissing-format-attribute -Wformat=2 -Wformat-security -Wformat-nonliteral -Wno-long-long -Wno-strict-aliasing -Werror "
  Mangled CFLAGS           = "-I/opt/servers/include -L/opt/servers/lib -I/opt/servers/include/heartbeat  -Wall -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wdeclaration-after-statement -Wpointer-arith -Wwrite-strings -Wcast-qual -Wcast-align -Wbad-function-cast -Winline -Wmissing-format-attribute -Wformat=2 -Wformat-security -Wformat-nonliteral -Wno-long-long -Wno-strict-aliasing -Werror  -ggdb3 -funsigned-char"
  Libraries                = "-lbz2 -lz -lc -luuid -lrt -ldl /lib64/libuuid.so.1 -lltdl"
  RPATH enabled            = ""
  Distro-style RPMs        = "no"

Note: If you use the 'make install' method for installation you
also need to adjust '/etc/passwd' and '/etc/group' manually.

到这里 cluster-glue 和 heartbeat 都编译完成了。

然后老大说要用rpm装。。。我了个去。。。

然后centos 6.2的源里 cluster-glue、resource-agents、pacemaker 都有,就是没有heartbeat。 幸好heartbeat的source里提供了.spec 可以用rpmbuild来生成rpm包。

下面继续 heartbeat.rpm 的 build过程

首先把下载下来的heartbeat源码包解压一份,然后再把源码包(.tar.gz)复制到 /root/rpmbuild/SOURCES/heartbeat.tar.bz2

tar -jxf Heartbeat-3.0.5.tar.bz2
cp Heartbeat-3.0.5.tar.bz2 /root/rpmbuild/SOURCES/heartbeat.tar.bz2
cd Heartbeat-XXXXXX
# 解决依赖
yum install ncurses-devel openssl-devel gettext bison flex mailx cluster-glue-libs-devel docbook-dtds docbook-style-xsl
# 开始生成。 (-bb 只编译二进制rpm包 -bs 只编译源码rpm包 -ba 同时编译二进制和源码rpm包)
rpmbuild -ba heartbeat-fedora.spec

最后生成文件

Wrote: /root/rpmbuild/SRPMS/heartbeat-3.0.5-1.el6.src.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/heartbeat-3.0.5-1.el6.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/heartbeat-libs-3.0.5-1.el6.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/heartbeat-devel-3.0.5-1.el6.x86_64.rpm

 

未完未完。。。
中途被拉去解决别的问题,HA的部署工作被人接手掉了

Read: 288

修复zsh svn1.7 自动完成的问题

更新svn(subversion)到1.7后 zsh 的svn自动完成就傻X了。

错误: _arguments:comparguments:312: invalid argument: [–cl]:arg:

修改方法

打开文件:/usr/share/zsh/4.3.11/functions/_subversion

找到35行左右

${=${${${(M)${(f)”$(LC_ALL=C _call_program options svn help $cmd)”#(*Valid options:|(#e))}:#* :*}%% #:*}/ (arg|ARG)/:arg:}/(#b)-([[:alpha:]]) \[–([a-z-]##)\](:arg:)#/(–$match[2])-    $match[1]$match[3] (-$match[1])–$match[2]$match[3]}

改为

${=${${${(M)${(f)”$(LC_ALL=C _call_program options svn help $cmd)”#(*Valid options:|(#e))}:#* :*}%% #:*}/ (arg|ARG)/:arg:}/(#b)(-##)([[:alpha:]]##) \[–([a-z-]##)\](:arg:)#/(-    -$match[3])$match[1]$match[2]$match[4] ($match[1]$match[2])–$match[3]$match[4]}

 

参考资料:

http://www.zsh.org/mla/workers/2011/msg01448.html

Read: 281