标签为‘shell’的日志
23

首先我们先回顾下bash现有的重定向符号

1.重定向输入输出,目标是文件word

[n]<word    默认n为0
[n]>word    默认n为1
[n]>|word   默认n为1    noclobber选项有关,直接例子就明白它的用处了
[n]>>word   默认n为1
igi@igi-debian:~$ rm -f testfile
igi@igi-debian:~$ touch testfile
igi@igi-debian:~$ cat testfile
igi@igi-debian:~$ set -o noclobber
igi@igi-debian:~$ echo 2 >testfile
bash: testfile: cannot overwrite existing file
igi@igi-debian:~$ echo 2 >| testfile
igi@igi-debian:~$ cat testfile
2

2.重定向标准错误和标准输出到指定文件描述符

&>word      更通用
>&word
>word 2>&1

追加输出

&>>word     没有>>&word的表达方法
>>word 2>&1

3.Here Documents

 <<[-]word
   here-document
delimiter
-符号将删除所有行开头的tab符

4.Here Strings

<<<word

5.复制文件描述符

[n]<&word   默认n为0,如果为数字,必须得为打开的文件描述符
[n]<&-      关闭文件描述符

[n]>&word   默认n为1,如果为数字,必须得为打开的文件描述符
[n]>&-      关闭文件描述符

6.移动文件描述符

[n]<&digit- 默认n为0
[n]>&digit- 默认n为1

7.以读写方式打开文件描述符

[n]<>word   文件不在时会被创建

如果要深刻理解重定向,先要明白以下2点
1.shell(bash或者csh等)负责重定向,对于程序或者函数,这一切都是透明的,它只管输入输出,至于从哪输入哪输出,是shell解释器负责
2.shell命令解析过程中,在实际执行命令前,IO重定向先设置好

我们来看以下的例子

1.’echo 1 a1 >a2′ 与 ‘echo 1 >a2 a1′

igi@igi-debian:~$ echo 1 a1 >a2
igi@igi-debian:~$ cat a2
1 a1
igi@igi-debian:~$ rm a2
igi@igi-debian:~$ echo 1 >a2 a1
igi@igi-debian:~$ cat a2
1 a1

IO重定向是在命令执行前设置好,所以上面两种情况,最后的效果一样,bash先把输出重定向到a2文件,再执行’echo 1 a1′

2.’ls nothisfile >res 2>&1′ 与 ‘ls nothisfile 2>&1 >res’

igi@igi-debian:~/rtest$ ls nothisfile
ls: cannot access nothisfile: No such file or directory
igi@igi-debian:~/rtest$ ls nothisfile >res 2>&1
igi@igi-debian:~/rtest$ cat res
ls: cannot access nothisfile: No such file or directory
igi@igi-debian:~/rtest$ ls nothisfile 2>&1 >res
ls: cannot access nothisfile: No such file or directory
igi@igi-debian:~/rtest$ cat res
igi@igi-debian:~/rtest$ ls -1
a
b
c
res
igi@igi-debian:~/rtest$ ls -1 2>&1 >res
igi@igi-debian:~/rtest$ cat res
a
b
c
res

‘ls nothisfile >res 2>&1′,文件描述符1被重定向到文件res(本来是标准输出),然后再把文件描述符2重定向到文件描述符1(此时是文件描述符1指向文件res),最后执行”ls nothisfile”,产生错误,被送往文件描述符2,最后流向文件res。
‘ls nothisfile 2>&1 >res’,文件描述符2被重定向到文件描述符1(即标准输出:屏幕),然后再把文件描述符1重定向到文件res,结果是文件描述符2被重定向到标准输出,文件描述符1被重定向到文件res,最后执行”ls nothisfile”产生的错误就被送往屏幕。

3.’ls nothisfile a >&word’ 与 ‘ls nothisfile a >&123456′

igi@igi-debian:~/test/shell$ ls -1
a
igi@igi-debian:~/test/shell$ cat a
this is a
igi@igi-debian:~/test/shell$ ls nothisfile a >&word
igi@igi-debian:~/test/shell$ cat word
ls: cannot access nothisfile: No such file or directory
a
igi@igi-debian:~/test/shell$ ls nothisfile a >&123456
-bash: 123456: Bad file descriptor
igi@igi-debian:~/test/shell$ cat 123456
cat: 123456: No such file or directory

>&这个重定向符号,在前面有提到,“重定向标准错误和标准输出到指定文件描述符“ 和 “复制文件描述符“ 都有这个符号, 实际上“重定向标准错误和标准输出到指定文件描述符“ 是 “复制文件描述符“ 的一种特别情况, 即当 [n]>&word 的n省略 且 word不是数字时, 会重定向标准错误和标准输出 到指定文件。
“ls nothisfile a >&word” , 由于word不是纯数字, bash解析成 ”重定向标准错误和标准输出到指定文件描述符“, 效果相当于 ” ls nothisfile a >word 2>&1″
“ls nothisfile a >&123456″, 由于123456是纯数字, bash解析成 “复制文件描述符”, 相当于 ” ls nothisfile a 1>&123456″ , 但由于 “ 复制文件描述符 “规定 “如果为数字,必须得为打开的文件描述符”
所以发生了错误。

4.’ls a 1>&-’ 与 ‘ ls a >&1- ‘

igi@igi-debian:~/test/shell$ ls
a
igi@igi-debian:~/test/shell$ cat a
this is a
igi@igi-debian:~/test/shell$ ls a >&1-
a
igi@igi-debian:~/test/shell$ ls a 1>&-
ls: write error: Bad file descriptor

“ ls a >&1- “, >&1-属于“移动文件描述符” 提到的 “[n]>&digit-”, 用文件描述符digit替换掉文件描述符n, n描述符被关闭.n默认为1. “ls a >&1-”, 相当与 ” ls a 1>&1- “, 把文件描述符1替换掉原
文件描述符1, 然后关闭原文件描述符1, 没发生变化, 输出依然被送到屏幕
” ls a 1>&- “, >&- 属于 “复制文件描述符” 提到的”关闭文件描述符”, ” ls a 1>&- “, 关闭了文件描述符1, 在运行” ls a”, 由于输出默认都送到文件描述符1, 而它被关闭, 报”错误的文件描述符”

5.’ls a nothisfile 1>&2-’ 与 ‘ ls a nothisfile 1<&2- '

igi@igi-debian:~/test/shell$ ls -1
a
igi@igi-debian:~/test/shell$ ls a nothisfile 1<&2-
a
igi@igi-debian:~/test/shell$ ls a nothisfile 1>&2-
a
igi@igi-debian:~/test/shell$ exec 3<>test
igi@igi-debian:~/test/shell$ ls a nothisfile 1>&3-
ls: cannot access nothisfile: No such file or directory
igi@igi-debian:~/test/shell$ cat test
a
igi@igi-debian:~/test/shell$

‘ 1>&2- ‘ 与 ‘ 1<&2- ' 是一个效果的, [n]>&digit- 和 [n]<&digit- 这两个移动文件描述符的操作, 都是移动digit 到 n , 区别在n没有指定时, <&digit- 等于 0<&digit-, 而 >&digit- 等于 1>&digit-
移动文件描述符, 就是把描诉符digit的指向给描述符n, 然后关了digit. 例子中的exec很好的解释移动文件描述符的行为, 文件描述符3被定向到文件test, 然后1>&3-, 会使得1也定向到3所定向的文件test, 然后文件描诉符3被关闭, 效果就是标准输出被定向到test文件.

, , ,

22

在学校搞过ubuntu的镜像源,没想到工作后继续维护镜像源,可谓有缘,也是很是开心能为有需要的人尽点绵薄之力。在这个过程中,自己也学到很多

记得一开始的学校那个服务器跑的是win 2003,用xlight提供ftp服务,为了便于管理,给系统装上了cygwin。在一开始摸索中,尝试过lftp脚本去同步镜像源,但lftp必须所有文件都扫描一次,长时间的空闲连接就被服务器踢掉了。后来才知道普遍的做法是用rsync去同步,在debian网站上提供了一个写的比较完善的rsync脚本。 该脚本提供了文件锁,进程锁等完善的处理,稍微修改就非常合适用来同步各镜像源

空间需求

对于镜像服务器,基本上是IO密集型, 对磁盘空间需求很大,如果只需要提供常用的i386,amd64两种架构的软件包及常用版本

fedora需要300G左右
ubuntu需要250G左右
debian需要250G左右
gentoo需要100G左右
archlinux需要50G左右
centos需要150G左右
cygwin需要10G左右

除此之外,还需要预留一定空间以保证不被新发布的版本或者大软件包塞爆硬盘。

同步选择

一般来说,除非你服务器的空间几乎无限,不然就得考虑排除没必要的架构和版本。以debian为例
debian现有alpha、amd64、arm、armel、hppa、hurd-i386、i386、ia64、m68k、mipsel、mips、powerpc、s390、sh and sparc这么多架构的软件包,还有source代码包
debian现有oldstable,stable,testing,unstable,还有experimental版本的
如此多的架构和版本,我们要根据需要进行排除,rsync的exclude选项可以很好的完成这个任务,有时遇到服务器端有问题的文件或者目录,也可以通过exclude选项跳过去。

确定好需要同步的架构及版本后,就需要找寻合适的同步对象,一个发行版一般都有许多个镜像源,但只有部分提供rsync服务,在各自的官方网站一般都可以找到这些列表或者搭建镜像的帮助。镜像源之间是有一定的等级的,一般会有一个总的最高级别的服务器(简称根服务器),所有新的软件包会现出现在它上面,然后它下级的镜像源再来它这里同步,然后还有下下级的镜像源来同步。如果能直接同步根服务器,那是最好的,因为你能最快获得新的软件包,但由于各种原因,根服务器不一定对所有用户都开放rsync同步服务,而是只允许特定ip列表才能访问其rsync服务(大部分发行版是这么作的)。一般来说,主要是根据访问速度及对方服务器的软件包的滞后程度来选择同步对象,国内开放rsync的开源镜像不多,周边地区速度不错的有台湾和日本的服务器,其他地方的速度就一般

同步策略

根据需要写好rsync脚本后,就需要添加crontab,以保持自动更新,推荐做法是一天更新几次,部分小源可以一天更新一次,如果只想一天更新一次,可以把时间放在深夜,一般规律都是凌晨5,6点访问量最小,在这段时间附近开始更新可以避开负载高峰。

其他

debian系的源,源目录都有pool目录,它存放所有软件包,如果使用了debian的同步脚本,或者在rsync同步时分两步,先同步这个目录,同步完成后再同步其他目录,这样就可以有效的避免服务器同步时其他人访问会有一定几率出错的问题,还有rsync的delete-after及delay-update选项,都可以避免未完成的更新对用户造成影响

贴下我稍微修改及注释的debian那个同步脚本

#! /bin/sh
set -e

# 此脚本由 http://www.debian.org/mirror/anonftpsync 修改而来
# 需要原脚本可以自行下载
# CVS: cvs.debian.org:/cvs/webwml - webwml/english/mirror/anonftpsync
# Version: $Id: anonftpsync,v 1.43 2008-06-15 18:16:04 spaillar Exp $ 

#增加mirror名变量,方便修改log名、lock名、mail信息
MIRROR="opensuse"

RSYNC="/usr/bin/rsync"
TO="/home/mirror/opensuse"
RSYNC_HOST="rsync.opensuse.org"
RSYNC_DIR="opensuse-hotstuff-160gb"
LOGDIR="/home/mirror.scripts/log"

# ARCH_EXCLUDE 控制需要排除的架构的包
# 对于Debian,以下是包含的架构
# alpha, amd64, arm, armel, hppa, hurd-i386, i386, ia64, m68k, mipsel, mips, powerpc, s390, sh and sparc
# 一个比较特殊的值: source
# 它将排除源代码包
# 例子
# ARCH_EXCLUDE="alpha arm armel hppa hurd-i386 ia64 m68k mipsel mips s390 sparc"
#对于opensuse:
#这里应该是: x86_64 i586 i686 noarch source
ARCH_EXCLUDE=

# EXCLUDE变量将排除更多需要排除的包,非得以不建议使用这个变量
# The following example would exclude mostly everything:
#EXCLUDE="\
#  --exclude stable/ --exclude testing/ --exclude unstable/ \
#  --exclude source/ \
#  --exclude *.orig.tar.gz --exclude *.diff.gz --exclude *.dsc \
#  --exclude /contrib/ --exclude /non-free/ \
# "
EXCLUDE=

#mail地址
#同步完成后把log发到指定邮箱,需要修改exim配置
MAILTO=

# LOCK_TIMEOUT是一个时间锁,以分钟为单位,默认锁6小时,即360分钟.
# 脚本运行时将创建lock文件,以保证同时间内只有一个rsync再运行
# 不同同步脚本的lock不能互相影响,以不同文件名区分.
LOCK_TIMEOUT=360

# RSYNC代理设置,一般不需要设置
# RSYNC_PROXY="IP:PORT"
# export RSYNC_PROXY=$RSYNC_PROXY

# 帐号密码设置
# . ftpsync.conf
# export RSYNC_PASSWORD
# RSYNC_HOST=$RSYNC_USER@$RSYNC_HOST

# 检查各个重要变量是否为空
if [ -z "$TO" ] || [ -z "$RSYNC_HOST" ] || [ -z "$RSYNC_DIR" ] || [ -z "$LOGDIR" ] || [ -z "$RSYNC" ]; then
    echo "One of the following variables seems to be empty:"
    echo "TO, RSYNC_HOST, RSYNC_DIR or LOGDIR"
    exit 2
fi

#hostname变量,也可以手工指定
HOSTNAME=hostname -f
# HOSTNAME=mirror.domain.tld

#LOCK文件,绝对路径,建议放在统一目录,便于管理
LOCK="/home/mirror.scripts/lock/$MIRROR-Archive-Update-in-Progress-${HOSTNAME}"

# 临时目录,由rsync --delay-updates 参数决定
# 必须保留,以避免错误,同步时所有新下载的都自动存放在临时目录
TMP_EXCLUDE="--exclude .~tmp~/"

# 架构排除变量的展开语句
#以下是原脚本的,只适用debian系的部分发行版
#for ARCH in $ARCH_EXCLUDE; do
#   EXCLUDE=$EXCLUDE"\
#       --exclude binary-$ARCH/ \
#       --exclude disks-$ARCH/ \
#       --exclude installer-$ARCH/ \
#       --exclude Contents-$ARCH.gz \
#       --exclude Contents-$ARCH.diff/ \
#       --exclude arch-$ARCH.files \
#       --exclude arch-$ARCH.list.gz \
#       --exclude *_$ARCH.deb \
#       --exclude *_$ARCH.udeb "
#   if [ "$ARCH" = "source" ]; then
#       SOURCE_EXCLUDE="\
#       --exclude source/ \
#       --exclude *.tar.gz \
#       --exclude *.diff.gz \
#       --exclude *.dsc "
#   fi
#done
#以下是为opensuse专门修改的
for ARCH in $ARCH_EXCLUDE; do
    EXCLUDE=$EXCLUDE"\
        --exclude *.$ARCH.rpm"
    if [ "$ARCH" = "source" ]; then
        SOURCE_EXCLUDE="\
        --exclude source/"
    fi
done

#日志文件
LOGFILE=$LOGDIR/$MIRROR-mirror.log
# 可以使用下面的命名方式
# LOGFILE=$LOGDIR/$(echo $RSYNC_DIR | tr / _)-mirror.log
# LOGFILE=$LOGDIR/${RSYNC_DIR/\//_}-mirror.log

cd $HOME
umask 002

# 在第一次运行时创建trace文件,记录每次同步的时间记录
# 只在Debian的镜像中发现有此文件,其他发行版一般不需要
#if [ ! -d "${TO}/project/trace/" ]; then
#  mkdir -p ${TO}/project/trace
#fi

# 判断是否有同一脚本的rsync在运行,可以避免上一同步还没完而起多一个同步进程
if [ -f "$LOCK" ]; then
# Note: this requires the findutils find; for other finds, adjust as necessary
  if [ "find $LOCK -maxdepth 1 -cmin -$LOCK_TIMEOUT" = "" ]; then
# Note: this requires the procps ps; for other ps', adjust as necessary
    if ps ax | grep '[r]'sync | grep -q $RSYNC_HOST; then
      echo "stale lock found, but a rsync is still running, aiee!"
      exit 1
    else
      echo "stale lock found (not accessed in the last $LOCK_TIMEOUT minutes), forcing update!"
      rm -f $LOCK
    fi
  else
    echo "current lock file exists, unable to start rsync!"
    exit 1
  fi
fi

#生成lock
touch $LOCK

# 在部分非debian系统,需要用0代替exit
# 捕捉退出信号,以删除lock
# 脚本结尾也有一句同样效果的,这里起保证异常退出时能删除lock的作用
# 单rsync错误,还是顺序执行到最后的rm,然后再触发这里的trap
# 保证的是父进程的异常退出
trap "rm -f $LOCK" exit

# 我根据需要加的,可在手工运行脚本时,捕捉ctrl+c
# 这样能再按下ctrl+c后继续保存log及删除lock文件
trap "" 2

set +e

# 我根据需要加的,写个时间进log,方便查
date +["Start "%F" "%T] >> $LOGFILE

# debian的原脚本把rsync分两步,先同步poor的内容
# 其他发行版不需要这么做
# timeout参数能在出现io错误时自动结速脚本,而不卡住
# delay-updates参数:先把下载的数据放tmp目录,同步完再移到正确位置
# 必须加此参数,可以避免未同步完的不完整包被用户下载导致错误
# 对于第一次同步,建议增加--size-only及--ignore-existing,以增加同步速度(在经常断开的情况下)
# 对于想删除不需要的exclude文件情况,可以增加--delete-excluded及--force(force用来强制删除空的不必要目录)
$RSYNC --recursive -p --links --hard-links --times \
     --progress \
     --verbose \
     --delay-updates --delete-after \
     --timeout=3600 \
     $TMP_EXCLUDE $EXCLUDE $SOURCE_EXCLUDE \
     $RSYNC_HOST::$RSYNC_DIR $TO/ >> $LOGFILE 2>&1

#chtime.sh用来记录源正常完成更新的时间
if [ $? -eq 0 ]
then
    /home/mirror.scripts/bin/chtime.sh opensuse >> $LOGFILE 2>&1
fi

#写时间进trace文件,非debian系统不需要
#LANG=C date -u > "${TO}/project/trace/${HOSTNAME}"

#写结束时间进log
date +["End "%F" "%T] >> $LOGFILE

#寄送邮件
if [ -n "$MAILTO" ]; then
    mail -s "$MIRROR archive synced" $MAILTO < $LOGFILE
fi

#保存log
savelog $LOGFILE >/dev/null

#最后删除lock文件
rm $LOCK

, ,