作者: slackcode
pkgtools版本: pkgtools-12.1.0-noarch-7.tgz
我并不是什么高手牛人,我只是路过,觉得好玩,所以拿出来看看,希望与大家分享,共同学习
如有转载,请注明出处:)
———————————————————-
installpkg源文件请到这里:
http://darkstar.ist.utl.pt/pub/slackware/slackware-12.2/source/a/pkgtools/scripts/installpkg
# If installpkg encounters a problem, it will return a non-zero error code.
# If it finds more than one problem (i.e. with a list of packages) you’ll only
# hear about the most recent one. :)
# 1 = tar returned error code
# 2 = failed ‘gzip -l package’
# 3 = does not end in .tgz
# 4 = not a file
# 99 = user abort from menu mode
EXITSTATUS=0
installpkg执行结束后的返回值, 即$?
# So that we know what to expect…
umask 022        # 当前权限 与 (777 - 022)
TAR=tar-1.13
$TAR –help 1> /dev/null 2> /dev/null
if [ ! $? = 0 ]; then
    TAR=tar
fi这里是判断$TAR –help的返回值,命令执行成功一般都为0,失败为非0,$?即为这个返回值
if [ ! “LC_MESSAGES=C $TAR --version“ = “tar (GNU tar) 1.13
Copyright (C) 1988, 92,93,94,95,96,97,98, 1999 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by John Gilmore and Jay Fenlason.” ]; then
    echo “WARNING: pkgtools are unstable with tar > 1.13.”
    echo “         You should provide a \”tar-1.13\” in your \$PATH.”
    sleep 5
fi
installpkg定义tar-1.13为稳定版本, slackware 12.2有tar-1.13和tar-1.16.1
再看看tar是哪个版本
ls -l /bin/tar
-rwxr-xr-x 1 root root 233196 2006-12-14 13:37 /bin/tar
-rwxr-xr-x 1 root root 115036 2006-12-14 13:37 /bin/tar-1.13*
lrwxrwxrwx 1 root root      3 2008-12-09 23:00 /bin/tar-1.16.1 -> tar
从这个看来, tar即tar-1.16.1了
if [ ! “LC_MESSAGES=C $TAR --version | head -n 1“ = “tar (GNU tar) 1.13” ]不是更简单么?不过可能出于严谨,作者认为只有版本号对是不够的,还要判断整个version
另这个LC_MESSAGES=C是需要的,我使用系统默认的en_US发现有问题,我知道使用C就是使用127bit的ASCII码系统,至于为什么en_US会不行,希望有高人能回答一下
usage() {
 cat << EOF
Usage: installpkg [options] package_name
Installpkg is used to install a .tgz package like this:
   installpkg xf_bin.tgz
options:      -warn (warn if files will be overwritten, but do not install)
              -root /mnt (install someplace else, like /mnt)
              -infobox (use dialog to draw an info box)
              -menu (confirm package installation with a menu, unless
                    the priority is [required] or ADD)
              -ask (used with menu mode: always ask if a package should be
                   installed regardless of what the package’s priority is)
              -priority ADD|REC|OPT|SKP  (provide a priority for the entire
                    package list to use instead of the priority in the
                    tagfile)
              -tagfile /somedir/tagfile (specify a different file to use
                    for package priorities.  The default is “tagfile” in
                    the package’s directory)
EOF
}
这个东西大家应该很熟悉
tagfile这个东西用得比较少,如果你经常装slackware,并且想定制自己的安装过程,那么用tagfile可以省去每次都选择安装包的麻烦,可以参考:
http://www.linuxsir.org/bbs/thread117475.html
http://www.flaterco.com/kb/slackware.html
http://www.linuxsir.org/bbs/thread117475-2.html
http://www.justlinux.com/nhf/Distribution_Specific/Slackware_Linux/Slackware__Tagfiles.html
# Eliminate whitespace function:
crunch() {
  while read FOO ; do
    echo $FOO
  done
}
这个函数我测试才发现的,能去掉多余的空格,保证最多只保留一个空格
package_name() {
  STRING=basename $1 .tgz
  # Check for old style package name with one segment:
  if [ “echo $STRING | cut -f 1 -d -“ = “echo $STRING | cut -f 2 -d -“ ]; then
    echo $STRING
  else # has more than one dash delimited segment
    # Count number of segments:
    INDEX=1
    while [ ! “echo $STRING | cut -f $INDEX -d -“ = “” ]; do
      INDEX=expr $INDEX + 1
    done
    INDEX=expr $INDEX - 1 # don’t include the null value
    # If we don’t have four segments, return the old-style (or out of spec) package name:
    if [ “$INDEX” = “2” -o “$INDEX” = “3” ]; then
      echo $STRING
    else # we have four or more segments, so we’ll consider this a new-style name:
      NAME=expr $INDEX - 3
      NAME=”echo $STRING | cut -f 1-$NAME -d -“
      echo $NAME
      # cruft for later ;)
      #VER=expr $INDEX - 2
      #VER=”echo $STRING | cut -f $VER -d -“
      #ARCH=expr $INDEX - 1
      #ARCH=”echo $STRING | cut -f $ARCH -d -“
      #BUILD=”echo $STRING | cut -f $INDEX -d -“
    fi
  fi
}
其实这段代码我不是太理解,因为我只接触过new-style name
以pkgtools-12.1.0-noarch-7.tgz为例吧,new-style名字结构为: name-ver-arch-build.tgz构成
我以前试过自己构建tgz包,但没有遵循这个命名规则,removepkg时没能正确识别这个包
总得来说,这个函数的作用是取得这个包的名字
我的疑问是为什么不直接cut -f 1 -d -呢,而要多做这么多工作?
# Set the prefix for the package database directories (packages, scripts).
ADM_DIR=”$ROOT/var/log”
# If the directories don’t exist, “initialize” the package database:
for PKGDBDIR in packages removed_packages removed_scripts scripts setup ; do
  if [ ! -d $ADM_DIR/$PKGDBDIR ]; then
    rm -rf $ADM_DIR/$PKGDBDIR # make sure it’s not a symlink or something stupid
    mkdir -p $ADM_DIR/$PKGDBDIR
    chmod 755 $ADM_DIR/$PKGDBDIR
  fi
done#建立包数据库,这几个目录就是pkgtools的包管理数据库啦 #包括包的安装和删除信息,一些安装和删除需要的脚本等都在这里了,这个路径也叫/var/adm/
# Make sure there’s a proper temp directory:
TMP=$ADM_DIR/setup/tmp
# If the $TMP directory doesn’t exist, create it:
if [ ! -d $TMP ]; then
  rm -rf $TMP # make sure it’s not a symlink or something stupid
  mkdir -p $TMP
  chmod 700 $TMP # no need to leave it open
fi
建立临时目录
# usage(), exit if called with no arguments:
if [ $# = 0 ]; then
  usage;
  exit
fi
没有任何参数时
# If -warn mode was requested, produce the output and then exit:
if [ “$MODE” = “warn” ]; then
  while [ -f “$1” ]; do
    echo “#### Scanning the contents of $1…”
    mkdir -p $TMP/scan$$
    ( cd $TMP/scan$$ ; $TAR xzf - install ) < $1 2> /dev/null
    # tar xzf - install的意思是: 把$1里的install目录解压为当前目录
    # 我第一次看到这种用法, 感到很cool
    if [ -r $TMP/scan$$/install/doinst.sh ]; then
      if cat $TMP/scan$$/install/doinst.sh | grep ‘ rm -rf ‘ 1>/dev/null 2>/dev/null ; then
        cat $TMP/scan$$/install/doinst.sh | grep ‘ rm -rf ‘ > $TMP/scan$$/install/delete
        echo “The following locations will be completely WIPED OUT to allow symbolic”
        echo “links to be made. (We’re talking ‘rm -rf’) These locations may be files,”
        echo “or entire directories.  Be sure you’ve backed up anything at these”
        echo “locations that you want to save before you install this package:”
        cat $TMP/scan$$/install/delete | cut -f 3,7 -d ‘ ‘ | tr ‘ ‘ ‘/‘
        # 对于这句的处理我觉得挺有意思, pkgtools约定的格式用法
        # 向用户提示一下rm -rf的东东
      fi
      if [ -d $TMP/scan$$ ]; then
        ( cd $TMP/scan$$ ; rm -rf install ) 2> /dev/null
        ( cd $TMP ; rmdir scan$$ ) 2> /dev/null
      fi
    fi
    echo “The following files will be overwritten when installing this package.”
    echo “Be sure they aren’t important before you install this package:”
    ( $TAR tzvvf - ) < $1 | grep -v ‘drwx’
    echo
    shift 1
  done
  exit
fi
warn模式, 作用是模拟安装过程, 提示用户哪些操作要注意的
# Main loop:
# 好不容易, 终于来到installpkg主循环啦LOL
for package in $ ; do
  # 当你installpkg a b c d时, 那里$就是a b c d啦, 就是传入的所有包名字
  # If someone left off the .tgz, try to figure that out:
  # “$package”不可读且”$package.tgz”可读, 自己加上.tgz
  if [ ! -r “$package” -a -r “$package.tgz” ]; then
    package=$package.tgz
  fi
  # “shortname” isn’t really THAT short… it’s just the full name without “.tgz”
  shortname=”basename $package .tgz“
  packagedir=”dirname $package“
  # This is the base package name, used for grepping tagfiles and descriptions:
  packagebase=”package_name $shortname“
  # examples:
  # installpkg /root/bash-3.1.017-i486-2.tgz
  # shortname   =  bash-3.1.017-i486-2
  # packagedir  =  /root(如果是installpkg bash-3.1.017-i486-2.tgz, 这个结果是.)
  # packagebase =  bash, package_name是函数, 那句英文注释说明其作用了
  # Reject package if it does not end in ‘.tgz’:
  if [ ! -r “dirname $package/$shortname.tgz” ]; then
    EXITSTATUS=3
    if [ “$MODE” = “install” ]; then
      echo “Cannot install $package: package does not end in .tgz”
    fi
    continue;
  fi
  # Determine package’s priority:
  unset PRIORITY
  if [ “$USERTAGFILE” = “” ]; then
    TAGFILE=”dirname $package/tagfile”
  else
    TAGFILE=”$USERTAGFILE”
  fi
  if [ ! -r “$TAGFILE” ]; then
    TAGFILE=/dev/null
  fi
  if grep “^$packagebase:” “$TAGFILE” | grep ADD > /dev/null 2> /dev/null ; then
    PRIORITY=”ADD”
  elif grep “^$packagebase:” “$TAGFILE” | grep REC > /dev/null 2> /dev/null ; then
    PRIORITY=”REC”
  elif grep “^$packagebase:” “$TAGFILE” | grep OPT > /dev/null 2> /dev/null ; then
    PRIORITY=”OPT”
  elif grep “^$packagebase:” “$TAGFILE” | grep SKP > /dev/null 2> /dev/null ; then
    PRIORITY=”SKP”
  fi
  if [ “$PRIORITY” = “ADD” ]; then
    PMSG=”[ADD]”
  elif [ “$PRIORITY” = “REC” ]; then
    PMSG=”[REC]”
  elif [ “$PRIORITY” = “OPT” ]; then
    PMSG=”[OPT]”
  elif [ “$PRIORITY” = “SKP” ]; then
    PMSG=”[SKP]”
  else
    PMSG=””
  fi
上面的$packagebase派上用场了, 还是tagfile, 之前发过一些关于tagfile的介绍, 我本人没用过tagfile来安装过
ADD:  Install the package.
REC:  Prompt whether to install the package, default yes.
OPT:  Prompt whether to install the package, default no.
SKP:  Do not install the package.
  # Locate package description file:
  DESCRIPTION=”/dev/null”
  # First check the usual locations outside the package, since this is faster:
  # 现在基本都是带有txt文件,像bash-3.1.017-i486-2.txt
  for file in $packagedir/disk* $packagedir/package_descriptions $packagedir/$shortname.txt $packagedir/$packagebase.txt ; do
    if grep “^$packagebase:” “$file” 1> /dev/null 2> /dev/null ; then
      DESCRIPTION=”$file”
    elif grep “^$shortname:” “$file” 1> /dev/null 2> /dev/null ; then
      DESCRIPTION=”$file”
    fi
  done
  # If we still don’t have anything, look inside the package.  This requires a costly untar.
  if [ “$DESCRIPTION” = “/dev/null” ]; then
    # $$是当前的进程号,这样可以保证目录唯一性,下面将大量使用$$
    mkdir -p $TMP/scan$$
    ( cd $TMP/scan$$ ; $TAR xzf - install ) < $package 2> /dev/null
    if grep “^$packagebase:” “$TMP/scan$$/install/slack-desc” 1> /dev/null 2> /dev/null ; then
      DESCRIPTION=”$TMP/scan$$/install/slack-desc”
    elif grep “^$shortname:” “$TMP/scan$$/install/slack-desc” 1> /dev/null 2> /dev/null ; then
      DESCRIPTION=”$TMP/scan$$/install/slack-desc”
    fi
  fi
  # Simple package integrity check:
  if [ ! -f $package ]; then
    EXITSTATUS=4
    if [ “$MODE” = “install” ]; then
      echo “Cannot install $package: package is not a regular file”
    fi
    continue;
  fi
  # gzip -l一下,看看成功否
  gzip -l $package 1> /dev/null 2> /dev/null
  if [ ! “$?” = “0” ]; thenf
    EXITSTATUS=2 # failed gzip -l # failed gzip -l 不成功
    if [ “$MODE” = “install” ]; then
      echo “Cannot install $package: package is corrupt (failed ‘gzip -l $package’)”
    fi
    continue;
  fi
  # Collect the package information into a temp file:
  COMPRESSED=gzip -l $package | grep -v uncompressed_name | crunch | cut -f 1 -d ' '
  UNCOMPRESSED=gzip -l $package | grep -v uncompressed_name | crunch | cut -f 2 -d ' '
  COMPRESSED=”expr $COMPRESSED / 1024 K”
  UNCOMPRESSED=”expr $UNCOMPRESSED / 1024 K”
#  MD5SUM=md5sum $package | cut -f 1 -d ' '
  cat $DESCRIPTION | grep “^$packagebase:” | cut -f 2- -d : | cut -b2- 1> $TMP/tmpmsg$$ 2> /dev/null
  if [ “$shortname” != “$packagebase” ]; then
    cat $DESCRIPTION | grep “^$shortname:” | cut -f 2- -d : | cut -b2- 1>> $TMP/tmpmsg$$ 2> /dev/null
  fi
  # Adjust the length here.  This allows a slack-desc to be any size up to 13 lines instead of fixed at 11.
  # 这些要补足12行的原因,我估计是为了显示上的美观
  LENGTH=cat $TMP/tmpmsg$$ | wc -l
  while [ $LENGTH -lt 12 ]; do
    echo >> $TMP/tmpmsg$$
    LENGTH=expr $LENGTH + 1
  done
  echo “Size: Compressed: $COMPRESSED, uncompressed: $UNCOMPRESSED.” >> $TMP/tmpmsg$$
  # For recent versions of dialog it is necessary to add \n to the end of each line
  # or it will remove repeating spaces and mess up our careful formatting:
  cat << EOF > $TMP/controlns$$
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
EOF
  # 一行行合并这两个文件
  paste -d “” $TMP/tmpmsg$$ $TMP/controlns$$ > $TMP/pasted$$
  rm -f $TMP/controlns$$
  mv $TMP/pasted$$ $TMP/tmpmsg$$
  # Emit information to the console:
  if [ “$MODE” = “install” ]; then
    if [ “$PMSG” = “” ]; then
      echo “Installing package $shortname… “ # 不使用tagfile,应该都进到这个
    else
      echo “Installing package $shortname ($PMSG)… “
    fi
    echo “PACKAGE DESCRIPTION:”
    cat $DESCRIPTION | grep “^$packagebase:” | uniq
    if [ “$shortname” != “$packagebase” ]; then
      cat $DESCRIPTION | grep “^$shortname:” | uniq
    fi
  elif [ “$MODE” = “infobox” -a ! “$PRIORITY” = “SKP” ]; then # install non-SKP infobox package
    # infobox模式, 非SKP(install non-SKP infobox package)
    dialog –title “Installing package $shortname $PMSG” –infobox “cat $TMP/tmpmsg$$“ 0 0
  elif [ “$MODE” = “menu” -a “$PRIORITY” = “ADD” -a ! “$ALWAYSASK” = “yes” ]; then # ADD overrides menu mode unless -ask was used
    # menu模式, ADD, 非-ask(ADD overrides menu mode unless -ask was used)
    dialog –title “Installing package $shortname $PMSG” –infobox “cat $TMP/tmpmsg$$“ 0 0
  elif [ “$MODE” = “menu” -a “$USERPRIORITY” = “ADD” ]; then # install no matter what $PRIORITY
    # menu模式, installpkg指定为ADD(install no matter what $PRIORITY)
    dialog –title “Installing package $shortname $PMSG” –infobox “cat $TMP/tmpmsg$$“ 0 0
  elif [ “$MODE” = “menu” -a “$PRIORITY” = “SKP” -a ! “$ALWAYSASK” = “yes” ]; then # SKP overrides menu mode unless -ask used
    # menu模式, SKP, 非-ask(SKP overrides menu mode unless -ask used)
    rm -f $TMP/tmpmsg$$
    continue # next package 不安装,跳过
  elif [ “$MODE” = “infobox” -a “$PRIORITY” = “SKP” -a ! “$ALWAYSASK” = “yes” ]; then # SKP overrides infobox mode, too
    # infobox模式, SKP, 非-ask(SKP overrides infobox mode, too)
    rm -f $TMP/tmpmsg$$
    continue # next package 不安装,跳过
  else # we must need a full menu:
    # 构造一个带选择的dialog界面
    dialog –title “Package Name: $shortname $PMSG” –menu “cat $TMP/tmpmsg$$“ 0 0 3 \
    “Yes” “Install package $shortname” \
    “No” “Do not install package $shortname” \
    “Quit” “Abort software installation completely” 2> $TMP/reply$$
    if [ ! $? = 0 ]; then
      echo “No” > $TMP/reply$$
    fi
    REPLY=”cat $TMP/reply$$“
    rm -f $TMP/reply$$ $TMP/tmpmsg$$
    if [ “$REPLY” = “Quit” ]; then
      exit 99 # EXIT STATUS 99 = ABORT! 退出啦,返回值是99
    elif [ “$REPLY” = “No” ]; then
      continue # skip the package 跳过这个包
    fi
  fi
dialog部分理解不是太好,觉得逻辑有点乱,加上自己平时都直接installpk的,不带参数
理解的兄弟请留言一下
  # Test tarball integrity, and make sure we’re not installing files on top of existing symbolic links:
  # 这个地方测试一下这个tgz包能不能解压
  $TAR tzf $package 1> $TMP/tmplist$$ 2> /dev/null
  TARERROR=$?
  if [ ! “$TARERROR” = “0” ]; then
    EXITSTATUS=1 # tar file corrupt
    if [ “$MODE” = “install” ]; then
      echo “Unable to install $package: tar archive is corrupt (tar returned error code $TARERROR)”
    fi
    rm -f $TMP/tmplist$$
    continue
  fi
  # 第一次看用while能这么传进去的, 这里是看看将要安装的文件
  # 在当前系统的是否有同名并且为符号链接的, 要删掉
  cat $TMP/tmplist$$ | grep -v “/$” | while read file ; do
    if [ -L “$ROOT/$file” ]; then
      rm -f “$ROOT/$file”
    fi
  done
  rm -f $TMP/tmplist$$
  # Write the package file database entry and install the package:
  # 建立数据库, 都是由以shortname为文件名的文本文件组成
  # 就在/var/adm/packages或/var/log/packages下
  # 所以在这里你可以查得当前系统安装的软件包信息
  echo “PACKAGE NAME:     $shortname” > $ADM_DIR/packages/$shortname
  echo “COMPRESSED PACKAGE SIZE:     $COMPRESSED” >> $ADM_DIR/packages/$shortname
  echo “UNCOMPRESSED PACKAGE SIZE:     $UNCOMPRESSED” >> $ADM_DIR/packages/$shortname
  echo “PACKAGE LOCATION: $package” >> $ADM_DIR/packages/$shortname
#  echo “PACKAGE MD5SUM: $MD5SUM” >> $ADM_DIR/packages/$shortname
  echo “PACKAGE DESCRIPTION:” >> $ADM_DIR/packages/$shortname
  cat $DESCRIPTION | grep “^$packagebase:” >> $ADM_DIR/packages/$shortname 2> /dev/null
  if [ “$shortname” != “$packagebase” ]; then
    cat $DESCRIPTION | grep “^$shortname:” >> $ADM_DIR/packages/$shortname 2> /dev/null
  fi
  echo “FILE LIST:” >> $ADM_DIR/packages/$shortname
  ( cd $ROOT/ ; $TAR -xzlUpvf - ) < $package >> $TMP/$shortname 2> /dev/null
  # 这个tar参数好长啊, 常用的不说了, 说不常用的, 这个命令在tar-1.16.1有错误, tar-1.13没有问题
  # 初步估计是-U参数问题, tar-1.16.1的–help没有-U说明
  # 另外, 要注意的是, 如果installpkg
  # -l, –one-file-system    stay in local file system when creating archive
  #     这个东东好像比较难理解,可以参考:http://www.delorie.com/gnu/docs/tar/tar_97.html
  #     基本意思就是只解压在同一文件系统下, 至于这样做的意义, 个人认为是出于包完整性的考虑
  # -U, –unlink-first       remove each file prior to extracting over it, 先删除文件, 再解压
  # -p, –same-permissions   extract all protection information, 保持文件属性
  if [ “cat $TMP/$shortname | grep '^\./' | wc -l | tr -d ' '“ = “1” ]; then
    # Good.  We have a package that meets the Slackware spec.
    # tgz包里有个.目录, 不知这个算不算tgz包的特征啊, 从上面这句英文来看就是了
    cat $TMP/$shortname >> $ADM_DIR/packages/$shortname
  else
    # Some dumb bunny built a package with something other than makepkg.  Bad!
    # Oh well.  Bound to happen.  Par for the course.  Fix it and move on…
    # 这段英文有意思, 哈
    echo ‘./‘ >> $ADM_DIR/packages/$shortname
    cat $TMP/$shortname | grep -v ‘^./$’ | cut -b3- >> $ADM_DIR/packages/$shortname
    # 这一行我理解不了,不知为什么多了cut
  fi
  rm -f $TMP/$shortname
  if [ -x /sbin/ldconfig ]; then
    /sbin/ldconfig # 更动态链接绑定, 新加入的so文件需要这个命令来使其生效
  fi
  if [ -f $ROOT/install/doinst.sh ]; then
    if [ “$MODE” = “install” ]; then
      echo “Executing install script for $shortname…”
    fi
    ( cd $ROOT/ ; sh install/doinst.sh -install; ) # 执行安装包的安装脚本
  fi
  # Clean up the mess…
  if [ -d $ROOT/install ]; then # install是目录
    if [ -r $ROOT/install/doinst.sh ]; then # doinst.sh可读
      # 备份这个doinst.sh
      cp $ROOT/install/doinst.sh $ADM_DIR/scripts/$shortname
      chmod 755 $ADM_DIR/scripts/$shortname
    fi
    # /install/doinst.sh and /install/slack- are reserved locations for the package system.
    ( cd $ROOT/install ; rm -f doinst.sh slack- 1> /dev/null 2><1 )     rmdir $ROOT/install 1> /dev/null 2><1
  fi
  # If we used a scan directory, get rid of it:
  if [ -d “$TMP/scan$$” ]; then
    rm -rf “$TMP/scan$$”
  fi
  rm -f $TMP/tmpmsg$$ $TMP/reply$$
  if [ “$MODE” = “install” ]; then
    echo
  fi
done
exit $EXITSTATUS
感觉像流水账,但回头看看,这不就是shell脚本么?
其中的shell指令也有些巧妙,当我细读这个脚本时体会到了
边读边学,还看到了一些tar和while的不常用用法,见识多了,但还是有不懂的地方,希望热心的朋友们指出和提出意见
读完installpkg后,更感觉到slackware的slack
下一篇是《剖析slackware pkgtools之upgradepkg篇》