作者: 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篇》