Ubuntu Server 16.04(xenial)를 사용하고 있으며 지정된 수의 시스템이 시작된 후 한 번 명령을 실행한 다음 자동으로 삭제하고 싶습니다. 솔루션은 두 단계로 구성되는 것 같습니다.
에 명령을 추가합니다
/etc/bash.bashrc
.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
이러한 변경을 수행한 후에는 스크립트가 실행되지만 아무런 효과가 없습니다. 이유를 이해하기 위해 각 줄을 차례로 살펴보겠습니다.
echo='welcome'
이 줄은
echo
문자열을 저장하는 변수를 생성합니다welcome
. 이 명령은 출력을 생성하지 않습니다. 문자열을 인쇄하려면welcome
다음을 사용해야 합니다.echo
주문하다, 환경 변수가 아님명명 된예를 들어 "에코"echo welcome
.count='0'
이 줄은
count
값을 저장하는 변수를 생성합니다0
. 이는count
다음과 같다는 것을 의미합니다 .0
모든스크립트 반복.((count+1))
이 줄은 변수와 관련된 산술 표현식을 평가합니다
count
. 이는 전혀 영향을 미치지 않습니다. 네가 원한다면증가count 변수를 사용하면 비슷한 작업을 수행할 수 있습니다((count++))
. 또한 값을 올바르게 늘리더라도count
스크립트가 종료되면 이 변경 사항이 유지되지 않습니다. 게다가 당신이했다변경 사항을 유지하려면 이전 줄( )을 덮어쓰게 됩니다count=0
.if ((count>=5)); then rm -- "$0"; fi
count
이 줄은 변수가 .보다 크거나 같으면 스크립트 파일을 삭제합니다5
. 그러나count
Equals 만 있기 때문에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번 재부팅 후에 작업이 사라지기를 원합니다.
우리는 대체 솔루션을 생각해냈습니다. 새로운 솔루션은 여러 사용자가 동시에 작업할 수 있는 다중 사용자 솔루션입니다. 두 개의 시스템 전체 스크립트와 두 개의 사용자별 데이터 파일을 사용합니다.
~/.tasks
count: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
생성한 후 count
0으로 설정합니다. 그런 다음 1로 ((count+1))
증가합니다 count
.스크립트가 실행될 때마다 처음 실행된 것으로 생각합니다..
실행 간에 실행 횟수가 유지되려면 메모리 상주 변수가 아닌 영구 저장소(예: 디스크)에 저장해야 하며, 특히 프로세스별 변수가 아닌 곳에 저장해야 합니다. 별도의 파일에 저장하는 것이 가장 좋은 방법이지만 스크립트 자체에 저장하고 매번 스크립트 자체를 편집하도록 하는 것도 가능합니다. 예를 들어 명령은 , then 등 count=0
으로 편집됩니다 . @igal의 답변은 두 가지 가능성을 모두 다루고 있습니다.count=1
count=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