- Why does the «at» command always warn me that commands will be executed via sh?
- Combine and Execute Multiple Linux Commands
- 1. Overview
- 2. Why Combine Multiple Commands?
- 3. Concatenate Commands With “;”
- 4. Concatenate Commands Conditionally
- 4.1. Concatenate Commands With “&&“
- 4.2. Concatenate Commands With “||”
- 5. Group Commands
- 5.1. Grouping Commands Using “ “
- 5.2. Grouping Commands Using “( )“
- 5.3. The Difference Between “ ” and “( )“
- 5.4. When Do We Need to Group Commands?
- 6. Multiple Commands in Background
- 7. Conclusion
Why does the «at» command always warn me that commands will be executed via sh?
It serves as a good warning to those of us that don’t use bash as our shell, because we we’ll forget that a feature that’s in our day-to-day shell isn’t going to be available when this code is run at the appointed time.
username@hostname$ at 23:00 warning: commands will be executed using /bin/sh at> rm **/*.pyc at> job 1 at 2008-10-08 23:00
The use of ‘**’ there is perfectly valid zsh, but not /sbin/sh! It’s easy to make these mistakes if you’re used to using a different shell, and it’s your responsibility to remember to do the right thing.
@Spudd86 — Yes, but bash emulates Bourne with it’s called as /bin/sh, so even if /bin/sh is just a symlink it still behaves differently than «normal» Bash.
Does the warning have any harmful effect aside from being annoying? The man page doesn’t mention any way of turning it off, so I don’t think you can stop it from being emitted without rebuilding your at from source.
Now, if you want to just not see it, you can use at [time] 2>/dev/null to send it off to oblivion, but, unfortunately, the at> prompts are printed to STDERR for some reason (a bug, IMO — they really should go to STDOUT), so they’re also hidden by this.
It may be possible to work up some shell plumbing which will eliminate the warning without also eating the prompts, but
- my attempt at this ( at [time] 2>&1 | grep -v warning ) doesn’t work and
- even if you can find a combination that works, it won’t be suitable for aliasing (since the time goes in the middle rather than at the end), so you’ll need to either type it in full each time you use it or else write a wrapper script around at to handle it.
So, unless it causes actual problems, I’d say you’re probably best off just ignoring the warning like the rest of us.
Combine and Execute Multiple Linux Commands
The Kubernetes ecosystem is huge and quite complex, so it’s easy to forget about costs when trying out all of the exciting tools.
To avoid overspending on your Kubernetes cluster, definitely have a look at the free K8s cost monitoring tool from the automation platform CAST AI. You can view your costs in real time, allocate them, calculate burn rates for projects, spot anomalies or spikes, and get insightful reports you can share with your team.
Connect your cluster and start monitoring your K8s costs right away:
1. Overview
In this tutorial, we’ll see the different ways in which we can combine and execute multiple Linux commands efficiently. We’ll be using Bash for our examples, so there could be slight differences with other shells.
2. Why Combine Multiple Commands?
Executing commands one after the other in a command line is a regular activity for a Linux administrator. But, by doing so, we may face the following issues:
- The first command can take a long time to complete – one has to wait until then to run the second command
- We may miss a particular command when multiple commands are run one-by-one
- We may misspell a particular command, which may lead to unintended consequences
- It’s time-consuming and annoying to run multiple commands one-by-one
To prevent such situations and obtain the expected result, we can combine and execute multiple commands in the command line.
3. Concatenate Commands With “;”
The “;” operator executes all commands regardless of whether the previous ones failed or not.
After logging into the server, we can execute the following commands:
- Change to logs directory (using cd)
- List the latest ten log files (using ls and head)
- Print the disk usage of logs files (using du)
- Print the disk usage of all file systems (using df)
Nevertheless, we can concatenate all of the above tasks in a single line using the “;“ operator:
$ cd logs; ls -lt | head; du -sh ; df -h
However, we need to keep in mind that if one command in the command chain fails, the remaining commands will execute anyways. And this may produce unexpected outputs.
4. Concatenate Commands Conditionally
The above approach is not suitable for all situations. Such as executing commands based on the success or failure of the previous one. In such cases, we can use one of the following approaches.
4.1. Concatenate Commands With “&&“
The “&&” or AND operator executes the second command only if the preceding command succeeds.
Let’s say we’re in the home directory of the server. We have a particular folder archive_old, which has old and unnecessary files that consume a lot of disk space. Therefore, we’ll need to remove the contents of that folder to free disk space.
To do that, we can execute the following commands one-by-one:
This works fine if the folder name is correct. But, let’s assume that we misspelled the folder name accidentally, then the required directory won’t exist. Consequently, we’ll be still working in the home directory. Then, the rm command will execute even though the cd command failed. As a result, all contents of the home directory will be removed. This would be fatal.
Let’s see what happens in such a situation when we replace the “;“ operator with the “&&” operator:
This way the rm command won’t be executed. This is because the “&&” operator executes the rm command only if the cd command succeeds.
4.2. Concatenate Commands With “||”
The “||” or OR operator executes the second command only if the precedent command returns an error.
Let’s assume we’re creating a new shell script to archive log files. Before executing a new shell script, we need to make it executable. Let’s see how we can achieve this using the “||” operator.
First, we’ll check if it is executable, if it’s not we’ll make it executable using the chmod command:
$ [ -x "archive_logs.sh" ] || chmod +x archive_logs.sh
In this example, the chmod command executes only if the file archive_logs.sh is not executable.
5. Group Commands
In Bash, we can use both “ ” and “( )” operators to group commands.
In this section, we’ll address how to use them to group commands and understand the difference between them through examples. Also, we’ll discuss the everyday use-cases of grouping commands.
5.1. Grouping Commands Using “ “
The syntax to use curly braces “ ” to group commands is:
Note that the semicolon (or newline) following the command list is required.
Let’s see an example of grouping four commands using “ “:
$ < echo "Hi there"; pwd; uptime; date; >Hi there /tmp/test/baeldung 20:27:11 up 30 days, 5:36, 1 user, load average: 0.26, 0.59, 0.77 Wed 19 Aug 2020 08:27:11 PM CEST
5.2. Grouping Commands Using “( )“
The syntax to use parentheses “( )” to group commands is quite similar, except that the semicolon following the command list is optional:
Let’s group the same commands using “( )“:
$ ( echo "Hi there"; pwd; uptime; date ) Hi there /tmp/test/baeldung 20:34:05 up 30 days, 5:43, 1 user, load average: 0.97, 0.86, 0.81 Wed 19 Aug 2020 08:34:05 PM CEST
5.3. The Difference Between “ ” and “( )“
There is just one single difference between command grouping using “ ” and “( )“:
When we change variables in a subshell, the changes are not visible outside the subshell. Let’s see an example to understand the difference between the two grouping methods.
First, let’s group commands using “ “:
$ VAR="1"; < VAR="2"; echo "Inside group: VAR=$VAR"; >; echo "Outside: VAR=$VAR" Inside group: VAR=2 Outside: VAR=2
As the output above shows, we initialized a variable: VAR=”1″ and changed its value to “2″ in the group. The variable is updated outside the command group as well.
Secondly, let’s do the same test using “( )“:
$ VAR="1"; ( VAR="2"; echo "Inside group: VAR=$VAR" ); echo "Outside: VAR=$VAR" Inside group: VAR=2 Outside: VAR=1
This time, the variable is changed in a subshell. Therefore, the changes will not affect the outer shell.
Let’s see another example of the cd command:
$ pwd; < cd /etc; pwd; >; pwd /tmp/test/baeldung /etc /etc $ pwd; ( cd /etc; pwd ); pwd /tmp/test/baeldung /etc /tmp/test/baeldung
In this example, the first output is straightforward. However, in the second output, why was the directory change in “( )” not kept?
This is because the cd command sets the environment variable $PWD, and the pwd command reads the variable $PWD. When the cd command updated the variable $PWD in a subshell, the change is not visible in the outer shell.
5.4. When Do We Need to Group Commands?
Primarily, we need to group commands in two scenarios:
- Apply same redirections on a list of commands
- Apply logical operators on a list of commands
When commands are grouped, redirections may be applied to the entire command list:
$ ( echo "Hi there"; pwd; uptime; date ) > /tmp/output $ cat /tmp/output Hi there /tmp/test/baeldung 23:07:59 up 30 days, 8:17, 1 user, load average: 1.55, 1.95, 2.09 Wed 19 Aug 2020 11:07:59 PM CEST
We’ve learned that we can use && and || to concatenate commands conditionally. With the command group, we can execute a list of commands when the condition is satisfied.
Let’s say, we attempt to ping a website to check if it is alive. Only if the ping command fails, we want to send an SMS to admin and write a log message. To achieve it, we can group the sendSMS command and the writeLog command:
$ ping -c1 "some.website" 1>/dev/null 2>&1 || ( sendSMS && writeLog )
To simulate the execution, we’ll use echo commands to simulate the sendSMS and the writeLog commands, and let them always run successfully:
$ alias sendSMS='echo "sending SMS..(site is down!)..DONE"' $ alias writeLog='echo "writing logs..(site is down!)..DONE"'
Now let’s do some tests with the command group:
$ ping -c1 "site.can.never.reach" 1>/dev/null 2>&1 || ( sendSMS && writeLog ) sending SMS..(site is down!)..DONE writing logs..(site is down!)..DONE $ ping -c1 "www.google.com" 1>/dev/null 2>&1 || ( sendSMS && writeLog )
With the command group, the entire command executes as we expected, no matter whether the ping command succeeded or failed.
Next, let’s see what will happen if we don’t group the sendSMS and writeLog commands:
$ ping -c1 "site.can.never.reach" 1>/dev/null 2>&1 || sendSMS && writeLog sending SMS..(site is down!)..DONE writing logs..(site is down!)..DONE $ ping -c1 "www.google.com" 1>/dev/null 2>&1 || sendSMS && writeLog writing logs..(site is down!)..DONE
As the output above shows, without grouping the sendSMS and writeLog commands, if the site is down, the whole command works as we expect.
However, if the ping command succeeds, the writeLog command is executed anyway. This isn’t what we want.
Therefore, grouping commands may help us solve some problems neatly.
6. Multiple Commands in Background
To execute a single command in the background mode, we can use the “&” operator. But to execute multiple commands in the background, we can use one of two ways:
Let’s say we have a shell script, let’s call it execute_backup_db.sh, that backs up the application database. We need to check how much time this shell script takes to complete. Let’s use the date command before and after the execution of the shell script to get the total execution time. This shell script may take a long time to complete.
So let’s run this in the background using the “&“ operator and log the command output in a log file:
$ date; ./execute_backup_db.sh; date & > execute_backup_db.log
Using “&“ with “;” will only run the final date command in the background. But the first date command and the shell script run only in the foreground. Instead, to run all commands in background conditionally, let’s do:
$ date && ./execute_backup_db.sh && date & > execute_backup_db.log
To achieve the same with “;“, let’s use the “( )” operator:
$ (date ; ./execute_backup_db.sh ; date) & > execute_backup_db.log
7. Conclusion
In this tutorial, we saw the different ways in which we can combine and execute multiple commands in the command line efficiently.
Operator “;“ can be used if the execution of multiple commands is unconditional. Whereas operators “&&” and “||” can be used if the execution of the command is based on the success or failure of the previous command.
Moreover, we addressed two ways to group commands and discussed the difference between them.
Also, we saw how we could execute multiple commands in the foreground and the background.
However, to regularly run multiple commands in a particular order, we may need to put all those commands in a shell script.