지정된 횟수만큼 시스템을 시작할 때마다 명령을 실행하면 자동으로 삭제됩니다.

지정된 횟수만큼 시스템을 시작할 때마다 명령을 실행하면 자동으로 삭제됩니다.

Ubuntu Server 16.04(xenial)를 사용하고 있으며 지정된 수의 시스템이 시작된 후 한 번 명령을 실행한 다음 자동으로 삭제하고 싶습니다. 솔루션은 두 단계로 구성되는 것 같습니다.

  1. 에 명령을 추가합니다 /etc/bash.bashrc.

  2. x번 실행한 후 삭제 되도록 bash.bashrc,어떻게든.

bash.bashrc끝에 다음 명령을 추가하여 솔루션의 첫 번째 단계를 완료했습니다 .

echo "Welcome!"

Bash에서 이 작업을 수행할 수 있는 방법이 있나요?

업데이트 - 이것이 내가 시도했지만 실패한 것입니다.

cat <<-"EOF" > /opt/bootmsg.sh
    #!/bin/bash
    echo welcome!
    count='0'
    ((count++))
    sed -i -e "s/^count='[01234]'$/count='${count}'/" "$0"
    if ((count>=5)); then rm -- "$0"; fi
EOF
chmod +x /opt/bootmsg.sh

# Add in the end of  bash.bashrc:
# [ -f /opt/bootmsg.sh ] && /opt/bootmsg.sh

코드에서 잘못된 점을 발견하면 수정된 코드 버전으로 답변을 게시하고 내가 뭘 잘못하고 있는지 설명해주세요.

답변1

일반화하다

주요 문제는 스크립트가 실행된 횟수, 즉 연속적인 스크립트 실행 사이에 지속되는 방법을 추적하는 방법을 찾는 것입니다. 스크립트가 종료된 후에 환경 변수는 해당 값을 유지하지 않기 때문에 환경 변수를 사용하여 이 작업을 수행할 수 없습니다.

가장 확실한 방법은 이 숫자를 파일에 저장하는 것입니다. 스크립트는 파일에서 값을 읽은 다음 업데이트된 값을 다시 파일에 쓸 수 있습니다. 이 개수 정보를 저장하기 위해 두 번째 파일을 사용하지 않으려면 스크립트가 자체적으로 업데이트되도록 할 수 있습니다(예 sed: 두 가지 접근 방식을 모두 설명하기 위해 예제 솔루션을 제공했습니다.

솔루션에서 환경 변수를 업데이트하고 이를 사용하여 상태를 추적하려고 시도하지만 스크립트 실행 간에 환경 변수가 유지되지 않으므로 솔루션이 실패합니다.

부인 성명:명시적으로 요청되었기 때문에 단일 파일 솔루션의 예를 제시했지만 개인적으로 자체 수정 스크립트가 포함되지 않은 솔루션을 선호합니다. 일반적으로 자체 수정 스크립트는 안정성이 낮고 디버그하기 어렵고 이해하기 어려운 경향이 있습니다.


해결 방법 1: 파일을 사용하여 개수 저장

다음은 필요한 재시작 횟수를 추적하기 위해 파일을 사용하는 솔루션입니다.

#!/usr/bin/env bash
# /opt/welcome.sh

# Read number of remaining reboots from file
REBOOTS="$(head -1 /var/reboots)"

# If the number of reboots is positive then
# execute the command and decrement the count
if [[ "${REBOOTS}" -ge 0 ]]; then

    # Run some commands
    echo "Doing some stuff... ($(( ${REBOOTS} - 1 )) left)"
fi

# Decrement the reboot count
REBOOTS="$(( ${REBOOTS} - 1 ))"

# Update the file
echo "${REBOOTS}" > /var/reboots

# If we've run out of reboots then delete the files
if [[ ! "${REBOOTS}" -gt 0 ]]; then
    rm /var/reboots
    rm -- "$0"
fi

다음은 이 특정 스크립트의 실행 예입니다.

user@host:~$ echo 3 > /var/reboots

user@host:~$ bash /opt/welcome.sh 
Doing some stuff... (2 left)

user@host:~$ bash /opt/welcome.sh 
Doing some stuff... (1 left)

user@host:~$ bash /opt/welcome.sh 
Doing some stuff... (0 left)

user@host:~$ bash /opt/welcome.sh 
bash: /opt/welcome.sh: No such file or directory

해결 방법 2: 자체 수정 스크립트 사용

또는 count 변수를 스크립트 자체에 포함시키고 이를 로 업데이트할 수 있습니다 sed. 예를 들면 다음과 같습니다.

#!/usr/bin/env bash
# /opt/welcome.sh

# Read number of remaining reboots from file
declare REBOOTS=3

# If the number of reboots is positive then
# execute the command and decrement the count
if [[ "${REBOOTS}" -ge 0 ]]; then

    # Run some commands
    echo "Doing some stuff... ($(( ${REBOOTS} - 1 )) left)"
fi

# Decrement the reboot count
REBOOTS="$(( ${REBOOTS} - 1 ))"

# Update the script
sed -i -e "s/^declare REBOOTS.*\$/declare REBOOTS=${REBOOTS}/" "$0"

# If we've run out of reboots then delete the script
if [[ ! "${REBOOTS}" -gt 0 ]]; then
    rm -- "$0"
fi

첨부파일이 없어도 동일한 효과가 나타납니다.


실패한 솔루션 시도 분석

고쳐 쓰다:질문에 다음 해결 시도를 추가했습니다.

cat <<-"WELCOME" > /opt/welcome.sh
    #!/bin/bash
    echo='welcome'
    count='0'
    ((count+1))

    if ((count>=5)) then rm -- "$0" fi
WELCOME
chmod +x /opt/welcome.sh

# Add in the end of  bash.bashrc:
# [ -f /opt/welcome.sh ] && /opt/welcome.sh

당신은 왜 이 솔루션이 작동하지 않는지 묻습니다. 실행하려는 실제 스크립트는 다음과 같습니다.

#!/bin/bash

echo='welcome'
count='0'
((count+1))

if ((count>=5)) then rm -- "$0" fi

((count>=))rm -- "$0", i.e. you probably intended for you위의 코드를 실행하려고 할 때 직면한 첫 번째 (명백한) 문제는 다음과 같이 조건식 뒤와 if 문의 본문 뒤에 세미콜론이 누락되었다는 것입니다 .

if ((count>=5)); then rm -- "$0"; fi

이러한 변경을 수행한 후에는 스크립트가 실행되지만 아무런 효과가 없습니다. 이유를 이해하기 위해 각 줄을 차례로 살펴보겠습니다.

  1. echo='welcome'

    이 줄은 echo문자열을 저장하는 변수를 생성합니다 welcome. 이 명령은 출력을 생성하지 않습니다. 문자열을 인쇄하려면 welcome다음을 사용해야 합니다.echo 주문하다, 환경 변수가 아님명명 된예를 들어 "에코" echo welcome.

  2. count='0'

    이 줄은 count값을 저장하는 변수를 생성합니다 0. 이는 count다음과 같다는 것을 의미합니다 .0모든스크립트 반복.

  3. ((count+1))

    이 줄은 변수와 관련된 산술 표현식을 평가합니다 count. 이는 전혀 영향을 미치지 않습니다. 네가 원한다면증가count 변수를 사용하면 비슷한 작업을 수행할 수 있습니다 ((count++)). 또한 값을 올바르게 늘리더라도 count스크립트가 종료되면 이 변경 사항이 유지되지 않습니다. 게다가 당신이했다변경 사항을 유지하려면 이전 줄( )을 덮어쓰게 됩니다 count=0.

  4. if ((count>=5)); then rm -- "$0"; fi

    count이 줄은 변수가 .보다 크거나 같으면 스크립트 파일을 삭제합니다 5. 그러나 countEquals 만 있기 때문에 0이런 일이 발생하지 않습니다.

count해결책을 시도할 때 발생하는 근본적인 문제는 스크립트 실행 간에 값을 유지하는 방법에 대한 문제를 해결하지 못한다는 것입니다 . count각 실행 시마다 재설정합니다.0

스크립트 반복 간에 값을 유지하는 가장 확실한 방법은 파일에서 값을 읽은 다음 업데이트된 값을 해당 파일에 다시 쓰는 것입니다. 따라서 첫 번째 솔루션입니다.

자신을 단일 파일로 제한하려면 해당 파일의 특수 줄(프로그래밍 방식으로 식별할 수 있도록 쉽게 구별할 수 있는 줄)에 값을 저장하여 본질적으로 동일한 작업을 수행한 다음 스크립트를 가질 수 있습니다. 각 행의 값을 업데이트하기 위해 반복 후에 자체적으로 수정합니다. 따라서 두 번째 솔루션입니다.


최소한의 수정과 수정으로 해결하려는 시도

특정 솔루션이 독립 실행형(자체 수정) 파일로 작동하도록 하려고 추가했기 때문에 다음은 작동하는 데 필요한 변경 사항을 최소화한 스크립트의 수정된 버전입니다.

#!/bin/bash
echo welcome
count='0'
((count++))
sed -i -e "s/^count='[01234]'$/count='${count}'/" "$0"
if ((count>=5)); then rm -- "$0"; fi

/opt/welcome.sh게시물에서와 같이 저장하면 다음과 같이 테스트할 수 있습니다.

user@host:~$ bash /opt/welcome.sh
welcome

user@host:~$ bash /opt/welcome.sh
welcome

user@host:~$ bash /opt/welcome.sh
welcome

user@host:~$ bash /opt/welcome.sh
welcome

user@host:~$ bash /opt/welcome.sh
welcome

user@host:~$ bash /opt/welcome.sh
bash: /opt/welcome.sh: No such file or directory

추가 댓글

.bashrc또한 재부팅 시 스크립트가 실행되기를 원하지만 새 셸 세션을 열 때마다 실행될 수 있는 파일에서 스크립트를 호출한다고 말합니다 . 시작 시 스크립트를 실행하는 방법에는 여러 가지가 있습니다. 그 중 대부분은 특정 운영 체제에 따라 다릅니다.

자세한 내용은 다음 문서를 확인하세요.


마지막 해결책

댓글에서 심도 있는 토론을 한 후, 실제로 원하는 것은 작업 목록 변경에 대한 알림을 표시하는 스크립트라는 것이 분명해졌습니다.

재부팅 후 처음 로그인할 때 작업이 나타나기를 원합니다. 또한 5번 재부팅 후에 작업이 사라지기를 원합니다.

우리는 대체 솔루션을 생각해냈습니다. 새로운 솔루션은 여러 사용자가 동시에 작업할 수 있는 다중 사용자 솔루션입니다. 두 개의 시스템 전체 스크립트와 두 개의 사용자별 데이터 파일을 사용합니다.

  • ~/.taskscount:description콜론으로 구분된 쌍 목록 (각 작업당 하나씩) 을 저장하는 사용자별 파일입니다 .

  • ~/.reminder-flag 마지막 실행 이후 작업 알림이 표시되었는지 여부를 추적하는 사용자별 상태 파일입니다.

  • /usr/local/bin/update-task-counts.sh.tasks모든 개수를 줄이고 개수가 0인 작업을 제거하여 파일을 업데이트하는 쉘 스크립트입니다 .

  • reminder-flag/usr/local/bin/print-tasks.sh 파일을 확인 및 업데이트하고 모든 작업 설명을 인쇄하는 쉘 스크립트입니다 .

다음은 예제 ~/.tasks파일입니다:

5:This is task one.
3:This is task two.
1:Yet another task.

이 목록의 첫 번째 작업은 총 5번 나타나야 하고, 두 번째 작업은 총 3번 나타나야 하며, 마지막 작업은 한 번만 나타나야 합니다.

또한 이 파일을 읽고 업데이트하려면 스크립트가 필요합니다. 다음은 이를 수행할 수 있는 스크립트입니다.

#!/usr/bin/env bash

# /usr/local/bin/update-task-counts.sh

# Set the location of the task file
TASKFILE="${HOME}/.tasks"

# Set the location of the reminder flag file
REMINDFILE="${HOME}/.remind-tasks"

# Set a flag so that we know we need to print the task messages
echo 1 > "${REMINDFILE}"

# If there is no task file, then exit
if [[ ! -f "${TASKFILE}" ]]; then
    exit 0
fi

# Create a temporary file
TEMPFILE="$(mktemp)"

# Loop through the lines of the current tasks-file
while read line; do

    # Extract the description and the remaining count for each task
    COUNT="${line/:*/}"
    DESC="${line/*:/}"

    # Decrement the count
    ((COUNT--))

    # If the count is non-negative then add it to the temporary file
    if [[ "${COUNT}" -ge 0 ]]; then
        echo "${COUNT}:${DESC}" >> "${TEMPFILE}"
    fi
done < "${TASKFILE}" 

# Update the tasks file (by replacing it with the temporary file)
mv "${TEMPFILE}" "${TASKFILE}"

이 스크립트를 실행하면 작업 파일의 각 줄을 반복하고 각 작업의 개수를 줄인 다음 양수 개수의 작업만 포함하도록 작업 파일을 업데이트합니다.

그런 다음 작업 목록의 작업을 인쇄하는 스크립트가 필요합니다.

#!/usr/bin/env bash

# /usr/local/bin/print-tasks.sh

# Set the location of the task file
TASKFILE="${HOME}/.tasks"

# Set the location of the reminder flag file
REMINDFILE="${HOME}/.remind-tasks"

# If there is no task file, then exit
if [[ ! -f "${TASKFILE}" ]]; then
    exit 0
fi

# If the reminder flag isn't set, then exit
FLAG="$(head -1 ${REMINDFILE})"
if [[ ! "${FLAG}" -eq 1 ]]; then
    exit
fi

# Loop through the lines of the current tasks-file
while read line; do

    # Extract the description for each task
    DESC="${line/*:/}"

    # Display the task description
    echo "${DESC}"

done < "${TASKFILE}"

# Update the flag file so we know not to display the task list multiple times
echo 0 > "${REMINDFILE}"

마지막으로 해야 할 일은 두 스크립트가 모두 적절한 시간에 호출되는지 확인하는 것입니다. 재부팅 시 스크립트를 실행 하려면 update-task-counts.sh사용자의 crontab에서 이를 호출할 수 있습니다. 즉, crontab에 다음 줄을 추가합니다(예: 를 사용하여 crontab -e).

@reboot /bin/bash /usr/local/bin/update-task-counts.sh

이 cron 기술에 대한 자세한 내용은 다음 게시물을 참조하세요.

사용자가 처음 쉘 세션에 들어갈 때 스크립트가 실행 되도록 하려면 print-tasks.sh다음 행을 에 추가하여 사용자의 bash 프로필에서 스크립트를 호출할 수 있습니다 ~/.bash_profile.

bash /usr/local/bin/print-tasks.sh

이제 샘플 파일을 사용하여 다음 스크립트를 실행해 보겠습니다 ~/.tasks.

5:This is task one.
3:This is task two.
1:Yet another task.

실행하지 않고 알림을 활성화하는 방법은 다음과 같습니다 update-task-counts.sh.

user@host:~$ echo 1 > ~/.reminder-flag

print-task.sh스크립트를 수동으로 테스트하려면 스크립트를 두 번 실행하면 됩니다.

user@host:~$ bash /usr/local/bin/print-tasks.sh
This is task one.
This is task two.
Yet another task.

user@host:~$ bash /usr/local/bin/print-tasks.sh

user@host:~$

첫 번째 호출에서만 인쇄됩니다. print-task.sh상호 작용을 수동으로 테스트하려면 다음 update-task-counts.sh과 같이 함께 실행합니다.

이 파일을 사용하여 위 스크립트를 수동으로 실행하면 결과는 다음과 같습니다.

user@host:~$  bash update-task-counts.sh
user@host:~$  bash print-tasks.sh
This is task one.
This is task two.
Yet another task.

user@host:~$  bash update-task-counts.sh
user@host:~$  bash print-tasks.sh
This is task one.
This is task two.

user@host:~$  bash update-task-counts.sh
user@host:~$  bash print-tasks.sh
This is task one.
This is task two.

user@host:~$  bash update-task-counts.sh
user@host:~$  bash print-tasks.sh
This is task one.

user@host:~$  bash update-task-counts.sh
user@host:~$  bash print-tasks.sh
This is task one.

user@host:~$  bash update-task-counts.sh
user@host:~$  bash print-tasks.sh

user@host:~$

이 작업이 가능해야 합니다.

답변2

@igal이 이를 올바르게 수행하는 방법을 설명했습니다. 원본이 작동하지 않는 이유를 알고 싶습니다.

가장 먼저 깨달아야 할 것은 쉘과 환경 변수가각 프로세스에 로컬. (환경 변수는 하위 프로세스에 의해 상속되지만 하위 프로세스는 자체 변수 복사본을 가져오며 상위 프로세스의 변수를 공유하지 않습니다.) 이는 Welcome.sh 스크립트가 실행될 때마다 해당 변수가 기본적으로 clean slate - it 예를 들어 count마지막으로 스크립트가 실행되었을 때 무엇이 ​​설정되었는지 알아낼 수 있는 방법이 없습니다 .

스크립트가 실행될 때마다 count정의된 변수 없이 시작됩니다. 그런 다음 을 실행하고 count=0생성한 후 count0으로 설정합니다. 그런 다음 1로 ((count+1))증가합니다 count.스크립트가 실행될 때마다 처음 실행된 것으로 생각합니다..

실행 간에 실행 횟수가 유지되려면 메모리 상주 변수가 아닌 영구 저장소(예: 디스크)에 저장해야 하며, 특히 프로세스별 변수가 아닌 곳에 저장해야 합니다. 별도의 파일에 저장하는 것이 가장 좋은 방법이지만 스크립트 자체에 저장하고 매번 스크립트 자체를 편집하도록 하는 것도 가능합니다. 예를 들어 명령은 , then 등 count=0으로 편집됩니다 . @igal의 답변은 두 가지 가능성을 모두 다루고 있습니다.count=1count=2

그런데 원본 스크립트에 다른 문제가 있습니다. 첫째, 값 을 if count='5'테스트하는 대신count세트5로 갑니다. 비교하려면 if [ "$count" -ge 5 ]or if [[ "$count" -ge 5 ]]또는 과 같은 것이 필요합니다 if ((count>=5)). 바라보다배쉬 FAQ #31에 대한 [ ]정보 [[ ]]산술 표현식에 대한 BashFAQ을 위한 (( )).

둘째, 나는 귀하의 시스템이 어떻게 설정되어 있는지 잘 모르지만 부팅할 때마다 /etc/bash.bashrc가 실행되는 것을 원하지 않습니다. Linux에서는 모든 대화형 셸의 시작 파일입니다. 즉, 새 대화형 셸이 열릴 때마다 실행된다는 의미입니다. 호출하기에 더 적절한 위치를 찾으십시오(시스템/배포판/사용 중인 항목에 따라 다름).

셋째, 스크립트가 스스로 삭제된 후 /opt/welcome.sh오류가 발생합니다. [ -f /opt/welcome.sh ] && /opt/welcome.sh그것을 실행하려면 다음을 사용하십시오.존재하는 경우에만.

답변3

코드가 올바른 bash 코드라고 가정하면 코드가 작동하지 않는 데에는 이유가 있습니다. 한 실행에서 다음 실행까지 "count" 값을 저장하는 것은 없습니다. 스크립트가 실행될 때마다 값이 count로 설정됩니다 '0'. 이 문제를 해결하려면 다음과 같은 몇 가지 해결 방법을 제안합니다.

  • sed명령문이 , , ... 가 count='0'되도록 각 실행마다 스크립트 자체를 업데이트(사용)하도록 합니다.count='1'count='2'count='5'
  • 일부 외부 파일에 카운트 데이터 저장
  • 세 번째 해결 방법을 추가할 수 있습니다. 파일 이름에 개수를 유지합니다. 즉, 파일 이름을 바꾸면 각 실행마다 파일 이름을 Welcome.5,welcome.4...에서welcome.0으로 바꾸면 닫힌 상태로 유지됩니다.

간단히 말해서, 코드에 문제가 있는 것이 아니라 디자인에 문제가 있는 것입니다.

또한 코드에 몇 가지 문제가 있습니다(그러나 문제를 해결하는 것만으로는 충분하지 않음).

  • echo='welcome'아무것도 표시하지 않으며 단지 echo변수를 "Welcome"으로 설정합니다. 어쩌면 당신은 그것을 진심으로 생각합니다 echo Welcome.
  • ((count+1))추가 1하면 count어디에도 저장되지 않는 새로운 값이 생성됩니다. 내 생각에 당신은 count=$((count+1))또는 ((count+=1)).
  • count=0효과는 그대로네요count='0'

최종 조언: 자체 파괴적인 코드를 갖는 것은 나쁜 생각입니다. 테스트할 때 처음 작동할 때 자체적으로 삭제됩니다. 100% 작동한다고 확신하고 작업 버전의 복사본을 얻을 때까지 이름을 바꾸면 됩니다.

답변4

오토 델환영합니다.sh

mkwelcome.sh스크립트 가 있습니다 :

#!/bin/bash
WelcomeFile=./welcome.sh
cat <<-"EOWelcome" >$WelcomeFile
    #!/bin/bash
    echo Welcome
    count=0
    old=$count
    ((count++))
    sed "s/^count=$old/count=$count/" -i $0
    ((count>4))&&rm $0
    EOWelcome
chmod +x $WelcomeFile

참고: 표를 주의해서 사용하세요. 줄 시작 부분에 공백이 있으면 안 됩니다!

큰따옴표는 변수가 해석되지 않도록 합니다..

그런 다음 시도해 볼 수 있습니다.

./mkwelcome.sh 
./welcome.sh 
Welcome
./welcome.sh 
Welcome
./welcome.sh 
Welcome
./welcome.sh 
Welcome
./welcome.sh 
Welcome
./welcome.sh 
bash: ./welcome.sh: No such file or directory

그런 다음 다음에 추가하십시오 .bashrc.

[ -f /path/welcome.sh ] && /path/welcome.sh

좋은 변화

sed 's/^ \+/\t/' >mkwelcome.sh

#!/bin/bash
WelcomeFile=./welcome.sh
cat <<-"EOWelcome" >$WelcomeFile
    #!/bin/bash
    conWord=(first second third fourth fifth) 
    count=0
    echo Welcome to your ${conWord[count]} login!
    old=$count
    ((count++))
    sed "s/^count=$old/count=$count/" -i $0
    ((count>4))&&rm $0
    EOWelcome
chmod +x $WelcomeFile

Ctrl+d

./mkwelcome.sh 
[ -x ./welcome.sh ] && ./welcome.sh 
Welcome to your first login!
[ -x ./welcome.sh ] && ./welcome.sh 
Welcome to your second login!
[ -x ./welcome.sh ] && ./welcome.sh 
Welcome to your third login!
[ -x ./welcome.sh ] && ./welcome.sh 
Welcome to your fourth login!
[ -x ./welcome.sh ] && ./welcome.sh 
Welcome to your fifth login!
[ -x ./welcome.sh ] && ./welcome.sh 
[ -x ./welcome.sh ] && ./welcome.sh 

관련 정보