![为什么将 sudo umount /mnt 放入 shell 脚本中执行失败,但在命令行中执行成功?](https://linux22.com/image/228566/%E4%B8%BA%E4%BB%80%E4%B9%88%E5%B0%86%20sudo%20umount%20%2Fmnt%20%E6%94%BE%E5%85%A5%20shell%20%E8%84%9A%E6%9C%AC%E4%B8%AD%E6%89%A7%E8%A1%8C%E5%A4%B1%E8%B4%A5%EF%BC%8C%E4%BD%86%E5%9C%A8%E5%91%BD%E4%BB%A4%E8%A1%8C%E4%B8%AD%E6%89%A7%E8%A1%8C%E6%88%90%E5%8A%9F%EF%BC%9F.png)
当我在环境中执行以下脚本时Debian 12 live
,我发现在这个脚本中无法执行umount /mnt
。提示是/mnt : target is busy
.但是,脚本执行后,我能够sudo umount /mnt
在命令行上成功执行。我已经给这个脚本添加了777权限。问题是什么?
#!/usr/bin/bash
set +x
current_path=$(pwd)
echo $current_path
hdstr=/dev/mmcblk0
hdstr1=${hdstr}p1
hdstr2=${hdstr}p2
hdstr3=${hdstr}p3
root_path=/mnt
boot_path=${root_path}/boot
grub_path=${boot_path}/grub
efi_path=${boot_path}/efi
lib64_path=${root_path}/lib64
bin_path=${root_path}/bin
lib_path=${root_path}/lib
echo "debug:function definition"
execute_command() {
local command=$1
eval "$command"
local status=$?
if [ $status -eq 0 ]; then
echo "run the cmd:$command success"
else
echo "failed to run the cmd:$command"
exit 1
fi
}
add_env_for_chroot() {
echo "add env for chroot"
execute_command "sudo mkdir -p ${lib64_path}"
execute_command "sudo ln -s ${lib_path} ${lib64_path}"
execute_command "sudo cp ${lib_path}/* ${lib64_path}/ -nra"
}
change_root() {
echo "change root"
#execute_command "sudo mount --rbind /dev /mnt/dev"
#execute_command "sudo mount --rbind /proc /mnt/proc"
#execute_command "sudo mount --rbind /sys /mnt/sys"
execute_command "sudo mount -t proc proc /mnt/proc"
execute_command "sudo mount -t sysfs sys /mnt/sys"
execute_command "sudo mount -o bind /dev /mnt/dev"
execute_command "sudo mount --bind /run /mnt/run"
add_env_for_chroot
cat << EOF | sudo chroot ${root_path}
grub-install --target=x86_64-efi /dev/mmcblk0 --force --recheck --efi-directory=/boot/efi
exit
EOF
}
create_fstab() {
echo "create fstab"
UUID1=$(sudo blkid | grep '^/dev/mmcblk0p1' | awk -F 'UUID="' '{print $2}' | awk -F '"' '{print $1}')
UUID2=$(sudo blkid | grep '^/dev/mmcblk0p2' | awk -F 'UUID="' '{print $2}' | awk -F '"' '{print $1}')
UUID3=$(sudo blkid | grep '^/dev/mmcblk0p3' | awk -F 'UUID="' '{print $2}' | awk -F '"' '{print $1}')
#这里不替换sda
devName="\/dev\/mmcblk0p1"
UUIDStr="UUID=$UUID1"
execute_command "sed -i \"s/${devName}/${UUIDStr}/g\" /mnt/etc/fstab"
devName="\/dev\/mmcblk0p2"
UUIDStr="UUID=$UUID2"
execute_command "sed -i \"s/${devName}/${UUIDStr}/g\" /mnt/etc/fstab"
devName="\/dev\/mmcblk0p3"
UUIDStr="UUID=$UUID3"
execute_command "sed -i \"s/${devName}/${UUIDStr}/g\" /mnt/etc/fstab"
}
partition_format_mount() {
echo "begin to part"
####partition####
sudo /sbin/parted ${hdstr} mklabel gpt
sudo /sbin/parted ${hdstr} <<EOT 1>/dev/null 2>/dev/null || exit 1
rm 1
rm 2
rm 3
rm 4
mkpart primary fat32 1MiB 200MiB
set 1 esp on
mkpart primary ext4 200MiB 8200MiB
mkpart primary ext4 8200MiB 100%
quit
EOT
echo ""
####format####
echo "begin to format"
#execute_command "sudo partx ${hdstr} 1>/dev/null"
execute_command "sudo /sbin/mkfs.fat -F32 ${hdstr1} 1>/dev/null"
execute_command "sudo /sbin/mkfs.ext4 ${hdstr2} 1>/dev/null"
execute_command "sudo /sbin/mkfs.ext4 ${hdstr3} 1>/dev/null"
execute_command "sudo /sbin/fsck.vfat -v -a -w ${hdstr1} 1>/dev/null"
execute_command "sudo /sbin/fsck.ext4 ${hdstr2}"
execute_command "sudo /sbin/fsck.ext4 ${hdstr3}"
####mount####
execute_command "sudo mkdir -p ${root_path}"
execute_command "sudo mount ${hdstr3} ${root_path} 1>/dev/null"
execute_command "sudo mkdir ${boot_path} -p"
execute_command "sudo mount ${hdstr2} ${boot_path} 1>/dev/null"
execute_command "sudo mkdir ${efi_path} -p"
execute_command "sudo mount ${hdstr1} ${efi_path} 1>/dev/null"
echo "end part"
}
install_os(){
echo "begin to install grub"
execute_command "sudo mkdir -p ${grub_path}"
execute_command "sudo cp ${current_path}/grub_CRA.cfg ${grub_path}/grub.cfg"
#execute_command "sudo cp ${current_path}/x86_64-efi /usr/lib/grub/ -raf" #issue1
execute_command "sudo cp \"${current_path}/bzImage\" ${boot_path}/" #2024-1-19 修改bzImage路径
execute_command "sudo cp \"${current_path}/initrd.img-6.4.0-rt8\" ${boot_path}" #2024-1-19 修改initrd路径
execute_command "sudo cp \"${current_path}/rootfs.tar.gz\" ${root_path}"
cd ${root_path}
execute_command "sudo tar -vxf rootfs.tar.gz"
execute_command "echo \"y\" | sudo rm rootfs.tar.gz"
echo "begin to changerooot"
change_root
create_fstab
echo "start to umount"
execute_command "sudo umount ${hdstr1} 1>/dev/null"
execute_command "sudo umount ${hdstr2} 1>/dev/null"
execute_command "sudo umount /mnt/run"
#execute_command "sudo umount /mnt/{proc,sys,dev}"
execute_command "sudo umount /mnt/proc"
execute_command "sudo umount /mnt/sys"
execute_command "sudo umount /mnt/dev"
#cat << EOF | sudo chroot ${root_path}
#echo "To successfully umount /mnt, I don't know why, but it is useful."
#exit
#EOF
execute_command "sync"
#==========================my question in here=============================
sudo umount /mnt
#==========================================================================
# execute_command "sudo umount ${hdstr3} 1>/dev/null"
#sudo /sbin/fsck.vfat -a ${hdstr1}
#sudo /sbin/fsck.ext4 -a ${hdstr2}
#sudo /sbin/fsck.ext4 -a ${hdstr3}
execute_command "sync"
}
mainFunc(){
echo "start to burn"
partition_format_mount
install_os
}
mainFunc
答案1
您已经获得了相当于cd /mnt
脚本上方的内容,因此您位于尝试卸载的目录中。
这里还需要考虑其他一些大问题:
脚本中的几乎每个命令都在 下运行
sudo
,因此请考虑删除所有这些调用sudo
并要求脚本本身在 下运行sudo
。#!/bin/bash if [ "$(id -u)" -ne 0 ] then echo "Please re-run with sudo" >&2 exit 1 fi
或者甚至让脚本自动重新运行
sudo
:#!/bin/bash if [ "$(id -u)" -ne 0 ] then echo "Re-running with sudo" >&2 exec sudo "$0" "$@" exit 1 fi
您将命令存储在变量中。这可能会导致各种与引用相关的问题,而您使用
eval
.变量用于数据,函数用于命令:execute_command() { if "$@" then echo "ran $1 successfully" >&2 else echo "failed to run $1" >&2 exit 1 fi } execute_command sudo umount /mnt/proc
但实际上,这可以简单地通过在第一行设置
-e
(或者可能是-ex
,该-x
标志启用逐行跟踪)或使用set -e
and 可能set -x
不久之后来实现。例如,#!/bin/bash -ex echo "this line succeeds" echo "the next line will fail" false echo "the script has already stopped so you will not see this" exit 0
您没有对任何变量进行双引号。这意味着每当您使用它们时,它们都会受到分裂和通配符的影响。
”我已经给这个脚本添加了777权限" - 可能不需要允许其他用户编辑此脚本,因此不要放弃该权利。最多允许其他用户运行该脚本:
chmod u=rwx,go=rx ./your_script # Replace "./yourscript" with the script path
答案2
在卸载分区之前,您需要退出 chroot 环境。通过exit
命令。
使用 bash getopts
,使您的脚本接受卸载函数作为参数。或者使用trap
在 bash 脚本退出时执行函数:
unmount_fscks (){
commands_umount_fsck_here
}
trap 'unmount_fscks' EXIT