When you execute a command in Linux, it generates a numeric return code. This happens whether you're running the command directly from the shell, from a script, or even from an Ansible playbook. You can use those return codes to handle the result of that command properly.
What the return codes mean
When running commands at a shell prompt, the special variable $? contains a number that indicates the result of the last command executed.
[ Download now: A sysadmin's guide to Bash scripting. ]
A zero (0) means everything went fine. Anything else means there is a problem.
A value of 1 generically indicates that some error has happened.
$ who
admin2 :1 2022-03-15 10:14 (:1)
$ echo $?
0
$ who | grep thisstringdoesnotexist
$ echo $?
1
In the example above, I executed the who command, which showed that I am admin2.
Immediately after that, I executed echo $?, and it returned zero because the previous command was executed successfully.
Then I executed the same who command (which I know is working fine) and piped that to grep with the non-existing argument. This time the $? variable contains a 1.
Why? It's because the last command executed was grep, which returns 1 when it cannot find the argument in its input.
[ Free eBook: Manage your Linux environment for success ]
Here is another example:
$ls myfile.cfg
ls: cannot access 'myfile.cfg': No such file or directory
$echo $?
2
Here I tried to list a file that doesn't exist. Then when I entered echo $? I got 2, which is how the ls command indicates that the argument is not a file or directory name.
Customize the return code
You can also use the exit command from a shell script to customize the return code to the caller script.
The following script illustrates this:
#!/bin/bash
if [ ! -f myfile.cfg ];
then
echo The file does not exist and we display an error
exit 64
fi
echo The file exists and we will do something
echo "(Not really doing anything, but this is where we could do it)"
exit 0
$./myscrypt.sh
The file does not exist and we display an error
$echo $?
64
$touch myfile.cfg
$./myscrypt.sh
The file exists and we will do something
(Not really doing anything, but this is where we could do it)
$echo $?
0
In this script, I explicitly provide the exit code for the failed and for the successful cases. Some observations:
- If I do not explicitly use
exit 0, the return code frommyscript.shwill be the return code from the last command executed inside it. This could be what I want, but here I wanted to state the return code pretty clearly inside the script. - I used
64as an arbitrary return code for the error condition. The Advanced Bash-Scripting Guide offers some guidelines about exit code values.
Test the return code with a shell script
If you need to test the return code of a command you invoked on your shell script, you just need to test the $? variable immediately after the command executes.
#!/bin/bash
# A snipet from a shell script ...
# Next we will invoke a command or another shell script
./myscript.sh
RETURN=$?
if [ $RETURN -eq 0 ];
then
echo "The script myscript.sh was executed successfuly"
exit 0
else
echo "The script myscript.sh was NOT executed successfuly and returned the code $RETURN"
exit $RETURN
fi
In this example, after invoking my command or script, I saved the exit code from $? on a variable for further utilization. (Again, $? returns the status of the last command, so you need to be careful if you run more commands—even a simple echo.).
Test the return code with an Ansible playbook
It is recommended to avoid running shell commands from an Ansible playbook if there is an Ansible module that performs the same action. This is especially because with a module you have better odds of being idempotent.
[ Ready to start automating? Check out this Ansible quick start series. ]
To illustrate how to handle return codes from a shell command, check this simple playbook:
---
- name: Executes a shell script
hosts: localhost
gather_facts: no
tasks:
- name: Execute the shell script
shell: ./myscript.sh
ignore_errors: true
register: result
- name: Shows the result of executing the script
debug:
msg:
- "Return code...: {{ result.rc }}"
- "{{ result.stdout_lines }}"
This is the Ansible playbook executed when a script returns an error:
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match
'all'
PLAY [Executes a shell script] *****************************************************************************************
TASK [Execute the shell script] ****************************************************************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "./myscript.sh", "delta": "0:00:00.003315", "end": "2022-06-13 15:35:06.123759", "msg": "non-zero return code", "rc": 64, "start": "2022-06-13 15:35:06.120444", "stderr": "", "stderr_lines": [], "stdout": "The file does not exist and we display an error", "stdout_lines": ["The file does not exist and we display an error"]}
...ignoring
TASK [Shows the result of executing the script] ************************************************************************
ok: [localhost] => {
"msg": [
"Return code...: 64",
[
"The file does not exist and we display an error"
]
]
}
PLAY RECAP *************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
I defined the task to ignore the error, so in the next task, I can simply display the return code and message from the script. I could also have handled the return code in my Ansible playbook by using the result.rc variable with some combination of the assert module or adding the when condition to another task.
Wrapping up
Now you have some knowledge about the return codes from commands and scripts invoked by your shell scripts and Ansible playbooks. I hope this helps you handle your scripts more easily.
저자 소개
Roberto Nozaki (RHCSA/RHCE/RHCA) is an Automation Principal Consultant at Red Hat Canada where he specializes in IT automation with Ansible. He has experience in the financial, retail, and telecommunications sectors, having performed different roles in his career, from programming in mainframe environments to delivering IBM/Tivoli and Netcool products as a pre-sales and post-sales consultant.
Roberto has been a computer and software programming enthusiast for over 35 years. He is currently interested in hacking what he considers to be the ultimate hardware and software: our bodies and our minds.
Roberto lives in Toronto, and when he is not studying and working with Linux and Ansible, he likes to meditate, play the electric guitar, and research neuroscience, altered states of consciousness, biohacking, and spirituality.
유사한 검색 결과
Slash VM provisioning time on Red Hat Openshift Virtualization using Red Hat Ansible Automation Platform
Red Hat Ansible Automation Platform: Measuring Business Impact with Dashboard and Analytics
Technically Speaking | Taming AI agents with observability
Testing, PDFs, And Donkeys | Compiler: Stack/Unstuck
채널별 검색
오토메이션
기술, 팀, 인프라를 위한 IT 자동화 최신 동향
인공지능
고객이 어디서나 AI 워크로드를 실행할 수 있도록 지원하는 플랫폼 업데이트
오픈 하이브리드 클라우드
하이브리드 클라우드로 더욱 유연한 미래를 구축하는 방법을 알아보세요
보안
환경과 기술 전반에 걸쳐 리스크를 감소하는 방법에 대한 최신 정보
엣지 컴퓨팅
엣지에서의 운영을 단순화하는 플랫폼 업데이트
인프라
세계적으로 인정받은 기업용 Linux 플랫폼에 대한 최신 정보
애플리케이션
복잡한 애플리케이션에 대한 솔루션 더 보기
가상화
온프레미스와 클라우드 환경에서 워크로드를 유연하게 운영하기 위한 엔터프라이즈 가상화의 미래