Running multiple commands in one line in shell
Say I have a file /templates/apple
and I want to
- put it in two different places and then
- remove the original.
So, /templates/apple
will be copied to /templates/used
AND /templates/inuse
and then after that I’d like to remove the original.
Is cp
the best way to do this, followed by rm
? Or is there a better way?
I want to do it all in one line so I’m thinking it would look something like:
cp /templates/apple /templates/used | cp /templates/apple /templates/inuse | rm /templates/apple
Is this the correct syntax?
Solution 1:
You are using |
(pipe) to direct the output of a command into another command. What you are looking for is &&
operator to execute the next command only if the previous one succeeded:
cp /templates/apple /templates/used && cp /templates/apple /templates/inuse && rm /templates/apple
Or
cp /templates/apple /templates/used && mv /templates/apple /templates/inuse
To summarize (non-exhaustively) bash's command operators/separators:
-
|
pipes (pipelines) the standard output (stdout
) of one command into the standard input of another one. Note thatstderr
still goes into its default destination, whatever that happen to be. -
|&
pipes bothstdout
andstderr
of one command into the standard input of another one. Very useful, available in bash version 4 and above. -
&&
executes the right-hand command of&&
only if the previous one succeeded. -
||
executes the right-hand command of||
only it the previous one failed. -
;
executes the right-hand command of;
always regardless whether the previous command succeeded or failed. Unlessset -e
was previously invoked, which causesbash
to fail on an error.
Solution 2:
Why not cp
to location 1, then mv
to location 2. This takes care of "removing" the original.
And no, it's not the correct syntax. |
is used to "pipe" output from one program and turn it into input for the next program. What you want is ;
, which seperates multiple commands.
cp file1 file2 ; cp file1 file3 ; rm file1
If you require that the individual commands MUST succeed before the next can be started, then you'd use &&
instead:
cp file1 file2 && cp file1 file3 && rm file1
That way, if either of the cp
commands fails, the rm
will not run.
Solution 3:
Note that cp A B; rm A
is exactly mv A B
. It'll be faster too, as you don't have to actually copy the bytes (assuming the destination is on the same filesystem), just rename the file. So you want cp A B; mv A C
Solution 4:
Another option is typing Ctrl+V Ctrl+J at the end of each command.
Example (replace #
with Ctrl+V Ctrl+J):
$ echo 1#
echo 2#
echo 3
Output:
1
2
3
This will execute the commands regardless if previous ones failed.
Same as: echo 1; echo 2; echo 3
If you want to stop execution on failed commands, add &&
at the end of each line except the last one.
Example (replace #
with Ctrl+V Ctrl+J):
$ echo 1 &&#
failed-command &&#
echo 2
Output:
1
failed-command: command not found
In zsh
you can also use Alt+Enter or Esc+Enter instead of Ctrl+V Ctrl+J