使用spec文件語法創建一個rpm
How to create an RPM package/zh-hk
< How to create an RPM package
此頁面包含 Packaging:ScriptletSnippets 的 zh_CN 翻譯,由於不具有 ScriptletSnippets 的編輯權限,故在此保存翻譯。
Contents[hide]
|
RPM 打包腳本綜述
Rpm spec 文件有幾個部分,允許軟件包執行代碼來完成安裝和卸載操作。這些軟件包中的腳本片段大多用於更新系統信息。此頁面提供 RPM 腳本片段概述和一些常見軟件包的 spec 腳本片段的示例。更完整的腳本片段,請參閱 Maximum RPM book 。
默認 Shell
在 Fedora ,您可以直接使用默認的 bash shell (/bin/sh
)。因此,所有腳本片段可以安全地在 bash 中運行。
語法
基本語法在 %build, %install 以及 rpm spec 文件的其他部分都是一致的。腳本支持一個特殊標記, -p 允許腳本直接調用一個程序,而不必啟用 shell 來運行程序。(即 %post -p /sbin/ldconfig
腳本片段還傳遞一個參數,用於表示本軟件包的個數。執行特定動作時,通過向 $1 傳遞不同值,來表示不同動作(安裝/升級/卸載),除了 %pretrans 和 %posttrans 它們的 $1 為 0 (rpm 4.4+ 支持 %pretrans 和 %posttrans)。對於安裝、升級和卸載,所傳遞的參數值如下表所示:
安裝(install) | 升級(update/upgrade) | 卸載(remove/erase) | |
%pretrans | $1 == 0 | $1 == 0 | (N/A) |
%triggerprein | 安裝本包: $1 == 0, $2 == 1 安裝目標包: $1 == 1, $2 == 0 |
$1 == 1, $2 == 1 | (N/A) |
%pre | $1 == 1 | $1 == 2 | (N/A) |
%post | $1 == 1 | $1 == 2 | (N/A) |
%triggerin | $1 == 1, $2 == 1 | 升級本包: $1 == 2, $2 == 1 升級目標包: $1 == 1, $2 == 2 |
(N/A) |
%triggerun | (N/A) | $1 == 1, $2 == 1 | 卸載本包: $1 == 0, $2 == 1 卸載目標包: $1 == 1, $2 == 0 |
%preun | (N/A) | $1 == 1 | $1 == 0 |
%postun | (N/A) | $1 == 1 | $1 == 0 |
%triggerpostun | (N/A) | 升級目標包: $1 == 1, $2 == 1 | 卸載目標包: $1 == 1, $2 == 0 |
%posttrans | $1 == 0 | $1 == 0 | (N/A) |
註意,如果安裝相同軟件包的多個版本,這些參數值將會不同(這發生於同時安裝包,如 kernel 和 multilib 包。然而,它會引發錯誤,防止軟件包升級完成)。所以,使用以下結構的腳本是個好主意:
%pre if [ $1 -gt 1 ] ; then # -gt大於 fi
...對於 %pre 和 %post 腳本,檢查它的值等於 2。
除了一些特殊情況(如果有的話),我們希望所有的腳本退出時返回 0。因為 rpm 默認配置不使用 -e
參數執行 shell 腳本,不包括明確的 exit
調用(可能出現非 0 返回值),在腳本中,最後一個命令的退出狀態決定了腳本片段的退出狀態。大多數命令包含 "|| :
" 後綴,這會強制以 0 狀態退出,無論命令是否可以正常工作。通常,最重要的是在腳本片段的最後一個命令添加此後綴,或在腳本的最後一行添加 ":
" 或 "exit 0
" 命令。註意,根據情況,使用其他的錯誤檢測/預防措施可能更合適,事先運行一些命令進行檢查,檢查成功才執行之後的命令。
腳本片段以非 0 狀態退出會中斷安裝/升級/刪除操作,避免事務中的包執行進一步的操作(見腳本片段命令部分),這可以防止舊包被刪除,同時也在 rpmdb 留下了重復記錄,在文件系統上留下了無主文件。某些情況下,事務中的腳本片段執行失敗,可能導致部分安裝失敗(不會中斷)。這往往局限於,軟件包不影響事務繼續執行,而此時中斷安裝之後的某些包,會導致更嚴重的系統問題。
腳本片段命令
%pre 和 %post 腳本片段分別在軟件包安裝前和安裝後執行。%preun 和 %postun 腳本片段分別在軟件包卸載前和卸載後執行。%pretrans 和 %posttrans 腳本片段分別在軟件包事務開始和結束時執行。升級軟件包時,按如下順序執行腳本片段:
- 檢查軟件包依賴、下載軟件包和 DRPM
- (all)%pretrans:事務開始時,執行新軟件包的此段代碼
- ...... (操作其它軟件包) ......
- (any)%triggerprein:此包的新版本安裝之前,觸發此包或其他包的腳本(如果有)
- (new)%triggerprein:指定的其他軟件包安裝之前,觸發此腳本
- (new)%pre:執行新軟件包的 %pre 腳本
- ...... (安裝所有新文件) ......
- (new)%post:執行新軟件包的 %post 腳本
- (any)%triggerin:安裝此軟件包時,觸發此包或其他包的腳本(如果有)
- (new)%triggerin:安裝指定的其他軟件包時,觸發此腳本
- (old)%triggerun:卸載指定的其他軟件包的舊版本時,觸發此腳本
- (any)%triggerun:卸載此軟件包的舊版本時,觸發此包或其他包的腳本(如果有)
- (old)%preun:執行舊軟件包的 %preun 腳本
- ...... (刪除所有舊文件) ......
- (old)%postun:執行舊軟件包的 %postun 腳本
- (old)%triggerpostun:指定的其他軟件包的舊版本已卸載之後,觸發此腳本
- (any)%triggerpostun:此包的舊版本已卸載之後,觸發其他包的腳本(如果有,此包腳本不運行)
- ...... (操作其它軟件包) ......
- (all)%posttrans:事務結束時,執行新軟件包的此段代碼
- 驗證軟件包,Done
Trigger 示例:
Trigger 用於在操作指定包時,運行您包中的一些代碼。通常因為您的包使用其他包的服務,或為其他包提供服務。參考以下的 Trigger 部分。提供以下示例以幫助理解:
%triggerin -p /usr/bin/perl -- ruby > 2.0, perl > 5.20 # -p 指定其他解釋器
以下情況觸發此段代碼:
- 已安裝此包,ruby 或 perl 被安裝/升級時
- 已安裝 ruby 或 perl,此包被安裝/升級時
編寫腳本片段
以下是一些編寫高質量打包腳本的建議。
在腳本片段間保存狀態
有時腳本需要保存之前腳本的一些狀態,以便在之後運行腳本時使用。這常用於對腳本片段進行優化。例如:
- 如果
%posttrans
需要在軟件包升級時註銷一些信息,但舊軟件包刪除時,包含這些信息的文件也一並被刪除,%pre
或%post
腳本片段需要在文件中保存這些信息,以便%posttrans
腳本可以訪問。 - 如果我們只想讓
%posttrans
中的程序在每次事務時工作,我們需要編寫一個標誌文件,使%posttrans
執行相應動作。
為了解決這些問題,腳本片段需要輸出供 %posttrans
使用的信息。我們建議使用 %{_localstatedir}/lib/rpm-state/
子目錄保存信息。例如, 當安裝 eclipse 插件時,腳本需要在 %{_localstatedir}/lib/rpm-state/eclipse/
創建一個文件。 %posttrans
運行腳本檢查文件是否存在。如果存在,執行相應動作,並刪除文件。這樣,每次事務時腳本只執行一次操作。
%{_localstatedir}/lib/rpm-state/
目錄屬於 rpm 或 filesystem 軟件包。使用事務時,軟件包需要創建自己的 %{_localstatedir}/lib/rpm-state/
子目錄。
Macros
復雜的腳本片段可以記錄在 rpm 宏中。這有兩個好處:
- 標準包的作者只需要記住宏,不需要記住它復雜的內容。
- 宏的實現可能改變,而無需更新包。
編寫宏時,FPC 需要審閱宏(指南中記錄了打包者需要做什麽)。
原則上,宏不能包含腳本片段的起始標記(如 %pre
)。這也意味著,一個宏不能同時定義 %pre
和 %post
需要執行的動作。相反,需要編寫一個宏在 %pre
執行,編寫另一個宏在 %post
執行。此原則使所有 spec 文件可以以同樣的方式使用宏,即使它們已定義了 %pre
或 %post
。
Trigger
Trigger 用於在操作指定包時,運行您包中的一些代碼。通常因為您的包使用其他包的服務,或為其他包提供服務。Trigger 語法如下:
%trigger{un|in|postun} [[-n] <subpackage>] [-p <program>] -- <trigger>
有一個很好的例子。假設您正在為 Emacs 和 Xemacs 編輯器打包一個很好的插件。它可以和這些編輯器一起工作,但根據不同編輯器,需要做不同配置。安裝插件時,可以檢查 Emacs 和 Xemacs 是否已安裝,並配置插件。但是,如果用戶稍後安裝 Xemacs,那麽會發生什麽呢?您的插件在 Xemacs 不可用,除非用戶卸載並重裝插件。通過觸發器,可以告訴此包,“已安裝 Xemacs,需要執行插件配置”。怎麽樣,很贊吧!
%triggerin -- emacs # 以下情況執行:已安裝此包, emasc 被安裝/升級時;已安裝 emacs,此包被安裝/升級時。 # 安裝插件相關代碼 %triggerin -- xemacs # 以下情況執行:已安裝此包,xemasc 被安裝/升級時;已安裝 xemacs,此包被安裝/升級時。 # 安裝插件相關代碼 %triggerun -- emacs # 以下情況執行:已安裝此包, emacs 被卸載時;已安裝 emacs,此包被卸載時。 # 卸載插件相關代碼 %triggerun -- xemacs # 以下情況執行:已安裝此包,xemacs 被卸載時;已安裝 xemacs,此包被卸載時。 # 卸載插件相關代碼
rpm 向觸發器腳本傳遞了兩個參數。$1 表示腳本完成時本軟件包的個數。$2 表示腳本完成時被觸發的軟件包的個數。
代碼片段
共享庫
安裝共享庫需要運行 /sbin/ldconfig 來更新動態鏈接器的緩存文件。可調用以下語句:
%post /sbin/ldconfig %postun /sbin/ldconfig
僅需要運行 ldconfig 時,可使用 -p 選項避免運行 shell:
%post -p /sbin/ldconfig %postun -p /sbin/ldconfig
建議使用第二種方式,這樣會自動添加 /sbin/ldconfig 依賴到軟件包(另外,可以避免啟用不必要的 Shell 進程來執行腳本)。
用戶和組
這些問題在 separate page 討論。
服務
Init 腳本約定
完整 SysV 風格的 init 腳本指南: Packaging/SysVInitScript
腳本片段細節: Packaging/SysVInitScript#InitscriptScriptlets
GConf
GConf 是 GNOME 桌面目前使用的配置方案。程序使用的默認值保存在 [NAME].schemas 文件,並安裝至 %{_sysconfdir}/gconf/schemas/[NAME].schemas。gconf 守護進程註冊並監控這些配置,並在配置變化時通知應用程序。schema 文件中包含了每個值的解釋(使用 gconf-editor 查詢數據庫時顯示這些信息)。
在構建包的過程中,必須禁止安裝 schema,並在實際安裝時,使用 gconf 守護進程註冊 [NAME].schemas 值;在刪除時,註銷這些值。腳本片段包含以下 4 個步驟。
創建包時,禁止安裝 Gconf schema:
%install export GCONF_DISABLE_MAKEFILE_SCHEMA_INSTALL=1 make install DESTDIR=$RPM_BUILD_ROOT ...
GCONF_DISABLE_MAKEFILE_SCHEMA_INSTALL 環境變量在包構建過程中,禁止安裝 schema。另一種方法是通過 configure 標識設置:
%build %configure --disable-schemas ...
不幸的是,此選項需要上遊開發者修改 Makefile.am 進行處理 。如果 Makefile.am 未配置,此選項不會生效,需使用環境變量代替。
第 2 步:
BuildRequires: GConf2 Requires(pre): GConf2 Requires(post): GConf2 Requires(preun): GConf2 ... %pre %gconf_schema_prepare schema1 schema2 %gconf_schema_obsolete schema3
在這一部分,我們使用 2 個宏卸載/更新舊 schema。
%gconf_schema_prepare
用於任何 GConf schema。它需要卸載當前已安裝的 schema。它需要不包含路徑和後綴的空格分隔的 schema 列表。註意宏幕後的工作,這個宏在 %post
中只處理已更改的 Gconf schema。
%gconf_schema_obsolete
標記廢棄包之前提供的 schema。如果系統存在舊 schema,它會將其註銷。如果舊 schema 不存在,則不執行動作。此宏接受空格分隔的 schema 列表。以下示例可能用於軟件包已改名的情況。如果舊 schema 名為 foo.schemas
,新 schema 名為 foobar.schemas
,你應該使用:
%gconf_schema_prepare foobar %gconf_schema_obsolete foo
下一步,對新安裝的 schema 進行處理:
%post %gconf_schema_upgrade schema1 schema2
%gconf_schema_upgrade
接受以空格分隔的 schema 列表。在宏幕後,它實際註冊新版本的 schema 並註銷舊版本。
最後一步,在包刪除時註銷 schema:
%preun %gconf_schema_remove schema1 schema2
當包升級時,rpm 調用 %pre
腳本來註冊和註銷 schema。當包卸載時,使用 %preun
腳本。%gconf_schema_remove
接受空格分隔的 schema 列表,並提供刪除方法。
為已修改的宏重建包
當宏修改時,使用這些宏的軟件包必須重建,以適應這些變化。repoquery 命令用於查找包含 schema 的軟件包:
repoquery --whatprovides "/etc/gconf/schemas/*" |sort |uniq |wc -l
EPEL 說明
EPEL 不包含 macros.gconf2,所以請按此說明操作: Packaging:EPEL#GConf
GSettings Schema
GSettings是 GNOME 3 桌面的配置系統。它代替了 GNOME 2 中使用的 GConf 系統。GSettings 支持可插拔後端,本地的 GNOME 使用 DConf 存儲設置。GSettings API 和工具屬於 glib2 軟件包。
程序使用的 GSettings schema 文件保存在 %{_datadir}/glib-2.0/schemas 目錄。schema 文件是擴展名為 .gschema.xml 的 XML 文件。運行時,GSettings 使用已編譯的二進制緩存(平臺無關),緩存通過 glib-compile-schemas 創建。只要修改 schema,就需要運行 /usr/bin/glib-compile-schemas 更新緩存。
%postun if [ $1 -eq 0 ] ; then /usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : fi %posttrans /usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
gdk-pixbuf 加載器
gdk-pixbuf 庫屬於 gdk-pixbuf2 軟件包。它用於在 GNOME 中加載各種圖像格式。gdk-pixbuf 可通過載入模塊來擴展對圖像格式的支持。這些模塊必須安裝在 %{_libdir}/gdk-pixbuf-2.0/2.10.0/loaders。為了避免載入所有模塊,gdk-pixbuf 在 %{_libdir}/gdk-pixbuf-2.0/2.10.0/loaders.cache 文件中保存模塊信息緩存。當模塊更改時,需要調用 /usr/bin/gdk-pixbuf-query-loaders 程序更新緩存信息。gdk-pixbuf-query-loaders 包含 -32 和 -64 版本,用於生成對應架構的緩存。
維護緩存文件的腳本是:
%postun /usr/bin/gdk-pixbuf-query-loaders-%{__isa_bits} --update-cache &> /dev/null || : %post if [ $1 -eq 1 ] ; then # For upgrades, the cache will be regenerated by the new package‘s %postun /usr/bin/gdk-pixbuf-query-loaders-%{__isa_bits} --update-cache &> /dev/null || : fi
註意, %{__isa_bits} 宏根據軟件包架構返回 32 或 64。
GTK+ 模塊
GTK+ 工具包(gtk3)支持通過加載模塊來提供主題引擎,輸入方法,打印後端或其他功能。這些模塊必須安裝至 %{_libdir}/gtk-3.0 或 %{_libdir}/gtk-3.0/3.0.0 目錄。對於輸入法,GTK+ 使用 %{_libdir}/gtk-3.0/3.0.0/immodules.cache 緩存記錄輸入法模塊信息。當修改輸入法時,需要調用 gtk-query-immodules-3.0 更新緩存信息。gtk-query-immodules-3.0 包含 -32 和 -64 版本,用於生成對應架構的緩存。
維護緩存文件的腳本是:
%postun /usr/bin/gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || : %post if [ $1 -eq 1 ] ; then # For upgrades, the cache will be regenerated by the new package‘s %postun /usr/bin/gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || : fi
3.0版本的可執行文件,是由於 gtk2 包含相同功能的工具(gtk-query-immodules-2.0)。註意, %{__isa_bits} 宏根據軟件包架構返回 32 或 64。
GIO 模塊
GIO 共享庫屬於 glib2 軟件包。它是 GNOME 中的底層堆棧。GIO 可以通過模塊中的 extension points 實現擴展。這些擴展模塊必須安裝至 %{_libdir}/gio/modules。為了避免載入所有模塊,GIO 在相同目錄的 giomodule.cache 文件中記錄可用模塊的緩存信息。當模塊改變時,通過調用 gio-querymodules 程序更新緩存信息。 gio-querymodules 包含 -32 和 -64 版本,用於生成對應架構的緩存。
維護緩存文件的腳本是:
%postun /usr/bin/gio-querymodules-%{__isa_bits} %{_libdir}/gio/modules &> /dev/null || : %post # We run this after every install or upgrade because of a cornercase # when installing the second architecture of a multilib package /usr/bin/gio-querymodules-%{__isa_bits} %{_libdir}/gio/modules || :
註意, %{__isa_bits} 宏根據軟件包架構返回 32 或 64。
Texinfo
GNU 項目和許多其他程序使用 texinfo 文件格式保存文檔。這些 info 文件保存在 /usr/share/info/。安裝或刪除軟件包時,install-info 添加新文件至 info 索引中,並在卸載時刪除它們。
Requires(post): info Requires(preun): info ... %post /sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || : %preun if [ $1 = 0 ] ; then /sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || : fi
這兩個腳本使用 install-info 在安裝時將 info 頁的信息添加至 info 索引文件,並在卸載時刪除索引信息。 "|| :" 可防止在系統已配置為不安裝任何 %doc 文件,或使用只讀掛載 %_netsharedpath /usr/share時,命令執行失敗。
Scrollkeeper
在當前 Fedora 中, scrollkeeper 已被 rarian 代替。rarian 不需要使用腳本片段進行處理。關於 EPEL 源的進一步說明,請訪問 Packaging:EPEL#Scrollkeeper
desktop-database
當 desktop entry 包含 MimeType 關鍵字時,應添加以下腳本。
%post /usr/bin/update-desktop-database &> /dev/null || : %postun /usr/bin/update-desktop-database &> /dev/null || :
註意:對於 Fedora Core 5 之後的版本,使用相同的 mimeinfo 文件和 gtk-icon-cache。即 spec 文件不需要為此添加 Require desktop-file-utils 。對於舊發行版,應添加以下語句:
Requires(post): desktop-file-utils Requires(postun): desktop-file-utils
(訪問 http://bugzilla.redhat.com/180898 和 http://bugzilla.redhat.com/180899)
mimeinfo
當軟件包在 %{_datadir}/mime/packages 安裝 XML 文件時,需使用以下腳本。
%post /bin/touch --no-create %{_datadir}/mime/packages &>/dev/null || : %postun if [ $1 -eq 0 ] ; then /usr/bin/update-mime-database %{_datadir}/mime &> /dev/null || : fi %posttrans /usr/bin/update-mime-database %{?fedora:-n} %{_datadir}/mime &> /dev/null || :
註意,與 gtk-update-icon-cache 代碼類似,這些腳本只在安裝 shared-mime-info 軟件包時運行,但不需要指定 Requires: shared-mime-info 。如果未安裝 shared-mime-info,update-mime-database 不會運行。然而這並不重要,因為大部分系統默認安裝 shared-mime-info。
Icon 緩存
如果應用程序在 %{_datadir}/icons/
的子目錄(如 hicolor
)安裝圖標,則必須更新圖標緩存,以便菜單正常顯示圖標。這包括:更新圖標上層目錄的時間戳,並運行 gtk-update-icon-cache
。‘touch‘ 頂層目錄以便兼容 Icon theme specification 的環境可以刷新緩存,並且 gtk-update-icon-cache
的運行也需要基於目錄的時間戳。
註意,不需要為此添加依賴關系。如果 gtk-update-icon-cache 不可用,則不會更新圖標緩存,同上,如果 "touch" 不可用,則不會更新圖標緩存。不添加 gtk-update-icon-cache(即 gtk2 >= 2.6.0)或 "touch" 依賴,使軟件包(spec)對系統的兼容性更好。例如,舊發行版或最小化安裝方式,通常在 spec 文件、rpmdb 和源的元數據中不包含這些包的條目。
%post /bin/touch --no-create %{_datadir}/icons/hicolor &>/dev/null || : %postun if [ $1 -eq 0 ] ; then /bin/touch --no-create %{_datadir}/icons/hicolor &>/dev/null /usr/bin/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : fi %posttrans /usr/bin/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
Systemd
包含 systemd unit 文件的軟件包,需要使用腳本以確保妥善處理這些服務。默認服務可以啟用或禁用。確定哪些情況下可以啟動服務,請參考 FESCo 策略: Starting_services_by_default。升級包時,如果服務正在運行,則會重啟服務;如果服務未運行,則不啟動它。同時,如果服務當前被禁用,則服務不會啟用。
新軟件包
什麽是新軟件包?在這段文字中,一個新軟件包指不包含任何 SysV init 腳本文件的包。
Scriptlets
Fedora 18+ 之後的 systemd 軟件包,提供了一系列宏來幫助處理 systemd 服務。這些宏的功能相當於舊版本 Fedora 的啟動腳本,但它還加入了 systemd "presets" 支持,參考文檔: https://fedoraproject.org/wiki/Features/PackagePresets
註意不要使用 %systemd_requires 宏。
Requires(post): systemd Requires(preun): systemd Requires(postun): systemd BuildRequires: systemd [...] %post %systemd_post apache-httpd.service %preun %systemd_preun apache-httpd.service %postun %systemd_postun_with_restart apache-httpd.service
有些服務不支持重啟(如 D-Bus 和某些存儲守護進程)。如果你的服務不需要在升級時重啟,使用以下 %post
腳本代替以上腳本:
%postun %systemd_postun
如果你的軟件包包含的一個或多個 systemd unit,需要在安裝時默認啟用,它們 必須 符合 Fedora preset policy。
宏的細節信息,請參考以下鏈接:
http://cgit.freedesktop.org/systemd/systemd/tree/src/core/macros.systemd.in
http://www.freedesktop.org/software/systemd/man/daemon.html
將軟件包從 SysV init 腳本遷移至 Systemd Unit
當從包含 SysV init 腳本的軟件包升級至包含 systemd unit 文件的軟件包時,將使用新的管理策略,不遷移用戶之前的配置。因此,可以簡單的使用以上腳本,不必擔心將 SysV 的相關信息遷移到 systemd。
在發行版之間遷移軟件包嚴禁從 systemd 更新到一個不包含 systemd 的版本。由於遷移將重置系統管理服務。只允許在 Fedora 版本間進行遷移。
Shells
/etc/shells
配置文件用於控制應用程序是否可以作為系統的用戶登錄 shell。它包含用於系統的有效 shells。如果你制作了一個新的 shell 軟件包,你需要將 shell 添加至此文件。詳細參考: man 5 SHELLS
獲得更多信息。
由於此配置可以編輯,所以首先需要確定配置已包含相關路徑。如果路徑不存在,就需要查看 shell 的二進制文件路徑。自從 Fedora 17 應用 UsrMove Feature 後,/bin
作為 /usr/bin
目錄的軟鏈接,我們需要導出所有路徑至 /etc/shells
文件。以下腳本,以打包名為 "foo" 的 shell 為例:
%post if [ "$1" = 1 ]; then if [ ! -f %{_sysconfdir}/shells ] ; then echo "%{_bindir}/foo" > %{_sysconfdir}/shells echo "/bin/foo" >> %{_sysconfdir}/shells else grep -q "^%{_bindir}/foo$" %{_sysconfdir}/shells || echo "%{_bindir}/foo" >> %{_sysconfdir}/shells grep -q "^/bin/foo$" %{_sysconfdir}/shells || echo "/bin/foo" >> %{_sysconfdir}/shells fi %postun if [ "$1" = 0 ] && [ -f %{_sysconfdir}/shells ] ; then sed -i ‘\!^%{_bindir}/foo$!d‘ %{_sysconfdir}/shells sed -i ‘\!^/bin/foo$!d‘ %{_sysconfdir}/shells fi
參考
http://wiki.wenyinos.org/post-39.html
使用spec文件語法創建一個rpm