sudo umount /mnt가 쉘 스크립트에서는 실행되지 않지만 명령줄에서는 성공하는 이유는 무엇입니까?

sudo umount /mnt가 쉘 스크립트에서는 실행되지 않지만 명령줄에서는 성공하는 이유는 무엇입니까?

환경에서 다음 스크립트를 실행하면 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제거하려는 디렉터리에 있습니다.

여기서 고려해야 할 몇 가지 다른 중요한 질문이 있습니다.

  1. 스크립트의 거의 모든 명령은 에서 실행되므로 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
    
  2. 명령을 변수에 저장합니다. 이는 데이터에 변수를 사용하고 명령에 함수를 사용하는 동안 모든 종류의 참조 관련 문제를 일으킬 수 있습니다 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간단히 달성 할 수 있습니다. 예를 들어,-xset -eset -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
    
  3. 변수를 큰따옴표로 묶지 않았습니다. 즉, 사용할 때마다 분할 및 와일드카드의 영향을 받습니다.

  4. "이 스크립트에 777 권한을 추가했습니다." - 다른 사용자가 이 스크립트를 편집하도록 허용할 필요는 없으므로 해당 권한을 포기하지 마십시오. 최대 다른 사용자가 스크립트를 실행하도록 허용할 수 있습니다.

    chmod u=rwx,go=rx ./your_script    # Replace "./yourscript" with the script path
    

답변2

파티션을 마운트 해제하기 전에 chroot 환경을 종료해야 합니다. 명령 으로 exit.

bash를 사용하여 getopts스크립트가 제거 기능을 매개변수로 허용하도록 만듭니다. 또는 trapbash 스크립트가 종료될 때 실행되는 함수를 사용하십시오.

unmount_fscks (){
commands_umount_fsck_here
}

trap 'unmount_fscks' EXIT

관련 정보