특정 프로세스가 완료되기를 기다리는 동안 스피너 표시

특정 프로세스가 완료되기를 기다리는 동안 스피너 표시

명령줄에서 작업이 완료될 때까지 스피너를 표시하려면 어떻게 해야 합니까? 즉, 스크립트를 실행 중이고 스크립트가 실행되는 동안 스피너가 나타나도록 하고 스크립트가 완료되면 스피너가 사라지도록 하려면 이것이 작동합니다.

일반적인 스피너 코드는 다음과 같습니다.

i=1
sp="/-\|"
echo -n ' '
while true
do
printf "\b${sp:i++%${#sp}:1}"
done

명령이 실행되는 동안 스피너를 표시하고 명령이 작업을 완료하면 사라지도록 이전 스피너 코드를 명령에 연결하려면 어떻게 해야 합니까? 루프에 명령을 포함하면 스피너와 함께 루프가 발생합니다. 그렇다면 이 경우 해결책은 무엇입니까?

답변1

while루프가 실제 종료 명령을 모니터링 하도록 하십시오 . Linux 환경에는 각 PID에 대한 /proc 항목이 있다고 가정하지만 다른 방법으로 이를 분할할 수도 있습니다.

#!/bin/bash
# your real command here, instead of sleep
sleep 7 &
PID=$!
i=1
sp="/-\|"
echo -n ' '
while [ -d /proc/$PID ]
do
  printf "\b${sp:i++%${#sp}:1}"
done

답변2

다음과 같이 사용할 수 있는 또 다른 멋진 스피너가 있습니다.

spinner ping google.com
echo "ping exited with exit code $?"

spinner sleep 10
echo "sleep exited with exit code $?"

# or, to check out the themes quickly
while spinner sleep 1; do echo; done

총 12개의 테마가 있으며 하나는 무작위로 선택됩니다.

#!/bin/bash
# Shows a spinner while another command is running. Randomly picks one of 12 spinner styles.
# @args command to run (with any parameters) while showing a spinner. 
#       E.g. ‹spinner sleep 10›

function shutdown() {
  tput cnorm # reset cursor
}
trap shutdown EXIT

function cursorBack() {
  echo -en "\033[$1D"
  # Mac compatible, but goes back to first column always. See comments
  #echo -en "\r"
}

function spinner() {
  # make sure we use non-unicode character type locale 
  # (that way it works for any locale as long as the font supports the characters)
  local LC_CTYPE=C

  local pid=$1 # Process Id of the previous running command

  case $(($RANDOM % 12)) in
  0)
    local spin='⠁⠂⠄⡀⢀⠠⠐⠈'
    local charwidth=3
    ;;
  1)
    local spin='-\|/'
    local charwidth=1
    ;;
  2)
    local spin="▁▂▃▄▅▆▇█▇▆▅▄▃▂▁"
    local charwidth=3
    ;;
  3)
    local spin="▉▊▋▌▍▎▏▎▍▌▋▊▉"
    local charwidth=3
    ;;
  4)
    local spin='←↖↑↗→↘↓↙'
    local charwidth=3
    ;;
  5)
    local spin='▖▘▝▗'
    local charwidth=3
    ;;
  6)
    local spin='┤┘┴└├┌┬┐'
    local charwidth=3
    ;;
  7)
    local spin='◢◣◤◥'
    local charwidth=3
    ;;
  8)
    local spin='◰◳◲◱'
    local charwidth=3
    ;;
  9)
    local spin='◴◷◶◵'
    local charwidth=3
    ;;
  10)
    local spin='◐◓◑◒'
    local charwidth=3
    ;;
  11)
    local spin='⣾⣽⣻⢿⡿⣟⣯⣷'
    local charwidth=3
    ;;
  esac

  local i=0
  tput civis # cursor invisible
  while kill -0 $pid 2>/dev/null; do
    local i=$(((i + $charwidth) % ${#spin}))
    printf "%s" "${spin:$i:$charwidth}"

    cursorBack 1
    sleep .1
  done
  tput cnorm
  wait $pid # capture exit code
  return $?
}

("$@") &

spinner $!

답변3

이 셸 스크립트는 요구 사항을 충족해야 합니다.

#!/usr/bin/env bash

show_spinner()
{
  local -r pid="${1}"
  local -r delay='0.75'
  local spinstr='\|/-'
  local temp
  while ps a | awk '{print $1}' | grep -q "${pid}"; do
    temp="${spinstr#?}"
    printf " [%c]  " "${spinstr}"
    spinstr=${temp}${spinstr%"${temp}"}
    sleep "${delay}"
    printf "\b\b\b\b\b\b"
  done
  printf "    \b\b\b\b"
}

("$@") &
show_spinner "$!"

쉘 스크립트를 라는 파일에 저장한다고 가정하면 spinner다음과 같이 호출하여 sleep 10명령이 실행되는 동안 스피너를 표시할 수 있습니다.

$ spinner sleep 10

답변4

거기에는 멋진 스피너가 있으며 모든 답변이 정확할 것이라고 확신합니다. 특히 @Jeff Schaller의 답변이지만 개인적으로 개발자로서 저는 코드를 읽고 무슨 일이 일어나고 있는지 정확히 알 수 있는 것을 좋아합니다. 터미널을 실행할 때 모든 git 저장소를 임시 zip으로 복사하는 bash 스크립트를 원하고, 이에 어울리는 멋진 스피너도 원합니다. 내 코드가 가장 컴팩트한지는 잘 모르겠지만 확실히 잘 작동합니다. 간단하고 읽기 쉽습니다.

나는 bash에 대해 가장 잘 아는 사람은 아니지만 가장 큰 문제는 다음과 같습니다.

  • 백그라운드에서 while 루프를 실행합니다(CPU에는 비용이 많이 들지만 누가 알겠습니까?)
  • 아직도 커서를 움직일 수 있는데 짜증나네요
  • 여러 프로세스가 발생하기를 원하는 경우 어떻게 해야 할지 모르겠습니다.
function runCommand() { 
    load &                                             # calls the loading function
    local whilePID=$!                                  # gets the pid for the loop
    tar -czf ${zipFileToUpdate} ${directoryToBackUp} & # backs up files
    local backupPID=$!                                 # get's back up pid
    wait $backupPID                                    # waits for backup id
    kill $whilePID                                     # kills the while loop
    echo -ne "done"                                    # echos done and removes the spinner
}

function load() { # just a function to hold the spinner loop, here you can put whatever
    while true; do
        echo -ne "/\r"
        sleep .1
        echo -ne "-\r"
        sleep .1
        echo -ne "\ \r"
        sleep .1
        echo -ne "|\r"
        sleep .1
    done
}

runCommand

^ 여러 명령에 대해 언급한 마지막 문제에 관해 말하자면, 저는 개인적으로 모든 명령을 함수에 넣은 다음 해당 함수를 백그라운드에서 실행할 것이라고 생각합니다. 하지만 PID가 여러 개 있을 수 있습니다.

경험:

function allCommands() {
    command1;
    command2;
    command3;
    ...;
}

runCommands()그런 다음 함수 에서

function runCommand() { 
    load &                                             # calls the loading function
    local whilePID=$!                                  # gets the pid for the loop
    allCommands &                                      # run function w/ all cmds
    local allCmdPID=$!
    ...
}

이 변수는 allCmdPID명령 전환과 동일하지 않을 수 있습니다. 첫 번째 명령을 기다린 다음 다른 명령이 계속 실행되는 동안 로딩 루프를 종료할 수 있습니다. 가능한 해결책은 다음과 같습니다:

  • for 루프에서 명령 배열 가져오기
  • 명령의 pid를 가져옵니다.
  • 잠깐만요
  • 그런 다음 다음 명령으로 진행하십시오.

그러나 그것은 모두 매우 지루해 보입니다.

관련 정보