 Hi, welcome everybody. My name is Leonardo Gutierrez. In this talk, I want to show you how you can easily write robust batch scripts. There's a lot of interesting content that I want to get through in this quick talk. So I'm going to be covering a few techniques that you can easily implement in your projects. You have my contact information there. I'll be publishing a link with the slides in my Twitter account after this presentation. So a little bit about myself. I'm a senior systems engineer working for AuraSun. I've been there for seven years now, I think, using a lot of open source technologies. So let's get started. Okay, so what can go wrong here? First of all, please do not run that command. Otherwise, you will delete all your files. So please don't run it. The terrible situation, explain it in that if an issue could have been easily avoided using a single command. Let me try to explain what happened here. Basically, the ARM command is trying to use a variable that doesn't exist. Batch tries to expand the value of the variable, I'm sorry. It fails to do that. It ignores that the variable doesn't exist and uses a slash y card as the argument and well the files are gone. If you want to know what happened with that issue, you can go to the link in the slide and read the full conversation. Let's see another example of what came wrong if we ignore these kind of little things. Okay, here we have a similar scenario. If the locks variable doesn't exist, the shell will ignore that and the command will copy everything from the slash to the sender's locks directory. So you might have a disk space issue depending on what you have in your file system. Okay, here we have a full script example with the shebang and everything. So be really careful when you see code like this, especially with these commands that can be really dangerous. Okay, so how do we fix it? We can fix it very easily. We want to know when a variable is not set. For that, we just need to use the set shell built in with the non-set option. This will make your scripts to fail immediately if a variable is not set. If you're on the code in the slide, you could get an error. You're going to get an error saying something like steamroot on bound variable. And the last code snippet, that is another way of fixing that using shell parameter expansions. If the steamroot variable is not defined, the message is not defined. The command will be printed in the sender error, and the shell will exit with the non-zero code. So as you can see, it is very easy to protect ourselves with these little mechanisms with these shell built-ins. Right. What if any other error occurs? Ideally, we should either catch that error or handle it with the non-zero code. To fail immediately if an error occurs, we can use again the set shell built-in, but now with the error exit option. Let's see an example. As you can see here, we're trying to copy a file that doesn't exist. We have in the tier line cp not found, and we're trying to copy that file to the x file. We are using the error exit in the second line. So the cp command will finish with an error, and the ls command won't run. As you can see, this is a good way of finishing script execution earlier if something really bad happens. The last two command lines show you how you can bypass or ignore an error. And if you want to disable the behavior, you can use those options at the right of the slide, set plus o or exit or set plus e. This is another example where we are bypassing the error checking. You can use the same set shell built-in with the same option, but now instead of dash o, you use plus plus o. Once you're done with section that you don't want to check, you can enable it back. Okay, which one should we use? I have seen a lot of scripts in different code bases where they use both, and they are totally different. In some systems, the first shebang in the slide actually points to a simling pointing to the bash binary. In some other systems, you will see that being sh actually points to a six shell compliant shell. So you need to be really careful on what to use and which one to use. So as you can see in these commands in this slide, being bash is totally different to being a sage in some systems. So be really careful about that. So my suggestion here is that if you are writing bash shell scripts, then use a bash shebang. Do not use being a sage thinking that it will point to the bash shell. It might be a different case. I have seen those weird scenarios where the developers use being a sage event and their scripts don't behave as expected. Okay, this is a really similar scenario as shown before. Do not run your scripts with a sage and then your script unless you want to, of course. Here we might have the same problems as I mentioned before. What happens if I run that code in my system? Let's see what happens. Here we can see that the previous script didn't run as expected. Being a sage is pointing to something different than the bash shell. I think it's pointing to the bash shell. I'm not sure. Okay, if you want to know if you are using code that is totally specific to bash, you can use this ShekBashisms program. It will show you where you are using bashisms and change suggestions to make your scripts POSIX compliant. Okay, this is another good suggestion. Sometimes we want to concatenate the strings, right? In this specific example, the shell would try to expand the EMB variable and it won't find it. So whenever you can, use braces around your variable names to make it clear to avoid confusions. Also, that could have been easily fixed using the non-set option. So for example, in that code, that code snippet could fail with the non-set option. This error is also very common. As you can see in this example, we are using the classic sort and then the sort unique operations. But we are not doing anything here to make sure that the sort command finishes successfully before it passes the information to the unique command. So if you run that command, you will see that the life is good is printed to the terminal, even that the not found file doesn't exist. So what would be the output of the code example? You can imagine life is good is printed to the standard output and we don't want that. So how do we fix it? The solution is really easy. To fix it, we just need to use the set shell built in again, but now with the pipe fail option. That way, when the sort command is executed, all the code there will fail. The script will fail with the non-zero code. All right, another good suggestion. Variables in Bash, you know, they have global scope. It means that if you declare a variable inside a function, it will be available through the whole program. So we as programmers, we want to reduce the scope over variables, right? So they are only available in the scope where they are used or needed. So here in the example, we can see that they are variable. The client said the count function will be available even after we call that function. This is not a good thing. We want to reduce the scope. We want the variable i to live only in that count function. We want to reduce the scope. So how can we fix it? For that, in order to fix it, we just need to use the local shell. All right, so to fix it, we need to use another shell built in. Now we are going to use the local shell built in. So that will help us to reduce the scope over variables. And if you are using the, if you are already using the non-set option, that will make your script even better. Because you won't have null or unset variables around. So as you can see, we can use these little shell built-ins to make our scripts better and more robust. Okay, in Bash, as in all our languages, we can use conditional execution with the AND and OR operators. But sometimes we might have unexpected results due to not using these operators properly as in the example. That is a common situation where we want to simulate the if, else statements with conditional execution. So as you can see here in the example, we are trying to copy a file, the file.txt to a backup. We're just trying to create a backup of that file. And as you can see in the output below, that is executed successfully. So the backup predicts successfully message is printed to the console. But what if the next sentence fails? I mean, what if the sourcing of the const.emb file fails? Or what if that file doesn't exist? We wouldn't expect the code in the OR section to be executed. Well, that is actually executed as in the output. That happens due to how presidents of the law you call AND and OR operators work in Bash and some other languages. The good news is that we can fix that very easy. Let's see how. So to fix it, instead of using conditional execution, we just need to use a regular if, else statement as in the example. We can see in the output that error creating backup message is no longer in the output. We see backup created successfully. And then file does not exist. All right. So that is the expected output in this case. So the suggestion here is do not use conditional execution only for single sentences. Do not use it to simulate a need else statement because you might have unexpected results. This suggestion is not really for Bash, but it is something you should have into consideration if you use crown jobs in production environments. I'll read the question first. What can go wrong with the following code if the script triggers errors that are not redirected properly? Well, as you can see, we have a script or crown job that is executed every minute. What happened if the script that runs every minute fails? And we have the script content below. It's a really simple script. It just run a rm command against a file that doesn't exist. Okay, so let's run it to see what happens. If the script fails every minute, it will send an email to your account with the error output with some explanation. The problem with that is that you might have later a disk space issue due to all the emails generated in the bark dispartition depending on how you have your system configured. Okay, so let's see how you can fix that. The first option is to redirect all the script output to a log file that you can handle and live in a better way or analyze later. You could do that from the crown tab line as in the first code snippet or inside the script. And you could also set the main environment variable to empty to avoid sending any emails. Okay, another suggestion. You should always implement some error handling in critical parts in your script. In the example, we're trying to send a file to a server and then we think that everything went well. We print the message and then exit with a zero code. The problem is that if the script is executed automatically, let's say through crown, then we will notice that it failed. So just imagine how painful it will be to find out that this important or critical job has been failing for a while and you didn't notice that. This is the last suggestion. Use shell check whenever you can. Shell check is a tool that you can use to find potential bugs in your code. It will show you what is wrong with that and it will give you an explanation why the code is not right and how you can fix it. So use it whenever you can. It is a really useful tool and it will make your scripts definitely better. So that's it from my side. Thanks for your time. Thanks for listening. If you have any questions, feel free to ask. If you have any questions, feel free to ask in Twitter or email or use the conference platform. Okay, thank you so much. Bye bye.