我正在运行 Arch Linux 和 systemd。
在/etc/fstab
我有这样的东西:
LABEL=XYZ /mypath vfat noauto,[...]
/mypath/main /newplace none bind,noauto 0 0
我目前必须/newplace
使用两个命令进行安装:
mount /mypath
mount /newplace
我也必须umount
两者兼而有之。
我需要将其简化为一个mount
(或umount
)命令:
mount /newplace
由于现有脚本,我需要使用上面显示的确切mount
(和相关)命令。umount
澄清:
单个命令mount /newplace
应首先 mount /mypath
,然后 mount/newplace
并/mypath
应保持挂载状态。
该命令umount /newplace
应该先 umount/newplace
然后还卸载/mypath
。
我可以更改/etc/fstab
我的设备的一些其他详细信息。但我无法更改调用的脚本mount /newplace
。我也无法自动挂载,/newplace
因为除非运行脚本,否则它通常需要保持卸载状态。
我一直在阅读有关递归绑定挂载、共享、私有、从属和其他挂载选项的信息,但我还没有找到实现我所寻求的目标的方法。
更新:为了回应评论,这显然不是递归挂载,所以我将其称为“先决条件”挂载。我希望这个词是合适的。我想到了“反向递归安装”这个术语,但这看起来很糟糕。我认为“先决条件”是指必须先发生的事情,并且为必要条件提供持续的基础。一般来说,在继续执行必要条件的同时,不能忘记或删除该先决条件。
在这种情况下,/mypath
(prevention) 和/newplace
(Required) 在安装时都保持安装状态/newplace
,并且在调用 时它们都将被卸载(当然可能以相反的顺序)umount /newplace
。
理想的解决方案将使用 systemd、Python 3 或 Xonsh。 (Bash 脚本也是可以接受的。我没有安装 zsh 或其他 shell。)
答案1
如果您确实希望将 FS 的“主”子目录LABEL=XYZ
挂载在 上/newplace
,而不特别需要将整个 FS 挂载在 上/mypath
,则可以添加如下行:
LABEL=XYZ /newplace subdir subdir=main,srctype=vfat,noauto,... 0 0
并创建/sbin/mount.subdir
助手(由 调用mount /newplace
,并不意味着直接调用)作为脚本,如下所示:
#! /bin/zsh -p
(( EUID == 0 )) || exec sudo -- "$0" "$@"
PATH=/bin:/sbin:/usr/bin:/usr/sbin
# mount -t subdir -o subdir=foo -o otheroption source /dest calls us
# as mount.subdir source /dest -o rw,subdir=foo,otheroption
dev=${1?} dest=${2?} opts=( ${(s[,])4?} )
# extract mandatory subdir option
(( i = $opts[(I)subdir=*] )) || exit
subdir=${opts[i]#*=}
opts[i]=()
# extract optional srctype option
if (( i = $opts[(I)srctype=*] )); then
type=(-t "$opts[i]")
opts[i]=()
else
type=()
fi
tmpdir=$(mktemp -d) || exit
mounted()true
if
mount "$type[@]" -o "${(j[,])opts}" -- "$dev" "$tmpdir"
then
mount --bind -- "$tmpdir/$subdir" "$dest" || mounted()false
umount -- "$tmpdir"
fi && rmdir -- "$tmpdir" && mounted
它将整个 FS 挂载到 tmpdir 中,将子目录绑定挂载到目标位置,然后卸载并删除 tmpdir。
如果,正如您稍后的编辑中所阐明的,您确实希望安装/newplace
和,而不仅仅是为了在 中安装 FS 子目录的最终目标的一些中间步骤,您可以采取类似的方法,并且这会变得更容易,因为你只需要帮助程序来安装必备的文件系统:/mypath
/mypath
/myplace
LABEL=XYZ /mypath vfat noauto,[...]
/myplace/main /newplace prereq prereq=/myplace,noauto,... 0 0
并将/sbin/mount.prereq
助手创建为脚本,如下所示:
#! /bin/zsh -p
(( EUID == 0 )) || exec sudo -- "$0" "$@"
PATH=/bin:/sbin:/usr/bin:/usr/sbin
src=${1?} dst=${2?} opts=( ${(s[,])4?} )
# extract prereq options
for prereq in ${(M)opts:#prereq=*}; do
prereq=${prereq#*=}
mountpoint -q -- $prereq ||
mount -- $prereq ||
exit
done
opts=(${opts:#prereq=*})
exec mount --bind -o "${(j[,])opts}" -- "$src" "$dst"
注意卸载/mypath
后不会自动卸载。/newplace
与 POSIX shell 语法相同(因此甚至应该在 中工作bash
)可能如下所示:
#! /bin/sh -
[ "$(id -u)" -eq 0 ] || exec sudo -- "$0" "$@"
PATH=/bin:/sbin:/usr/bin:/usr/sbin
src="${1?}" dst="${2?}"
set -o noglob
IFS=,
set -- ${4?}
# extract prereq options
for opt do
case $opt in
(prereq=*)
prereq=${opt#*=}
mountpoint -q -- "$prereq" ||
mount -- "$prereq" ||
exit
;;
(*)
set -- "$@" "$opt"
esac
shift
done
opts="$*"
exec mount --bind -o "$opts" -- "$src" "$dst"
(未经测试)。
如果你不能编辑您的脚本,您可以对其进行检测。例如,如果它写成bash
,则将其称为:
bash -c '
mount() {
if [[ $1 = "/newplace" ]]; then
command mount /mypath || return
fi
command mount "$@"
}
export -f mount
exec "$0" "$@"' your-script its args
或者,如果它是用 编写的zsh
,则添加到~/.zshenv
(或为该脚本/some/dir/.zshenv
设置):ZDOTDIR=/some/dir
if [[ $ZSH_SCRIPT:P = /path/to/your-script ]]; then
mount() {
if [[ $1 = /newplace ]]; then
command mount /mypath || return
fi
command mount "$@"
}
fi
或者对于任何 shell,添加一个执行相同操作的mount
脚本:/some/dir
#! /bin/sh -
if [ "$1" = /newplace ]; then
/bin/mount /mypath || exit
fi
exec /bin/mount "$@"
并将您的脚本称为:
PATH="/some/dir:$PATH" your-script its args