1. 概要

このチュートリアルでは、複数のLinuxコマンドを効率的に組み合わせて実行するさまざまな方法を紹介します。 例ではBashを使用するため、他のシェルとはわずかに異なる可能性があります。

2. 複数のコマンドを組み合わせる理由

コマンドラインでコマンドを次々に実行することは、Linux管理者にとって通常のアクティビティです。 ただし、そうすることで、次の問題に直面する可能性があります。

  • 最初のコマンドは完了するまでに長い時間がかかる場合があります–2番目のコマンドを実行するにはそれまで待つ必要があります
  • 複数のコマンドを1つずつ実行すると、特定のコマンドを見逃す可能性があります
  • 特定のコマンドのスペルを間違える可能性があり、意図しない結果につながる可能性があります
  • 複数のコマンドを1つずつ実行するのは時間と手間がかかります

このような状況を防ぎ、期待される結果を得るために、コマンドラインで複数のコマンドを組み合わせて実行できます。

3. コマンドを「;」で連結します

「;」 オペレーターは、前のコマンドが失敗したかどうかに関係なく、すべてのコマンドを実行します。

サーバーにログインした後、次のコマンドを実行できます。

  • ログディレクトリに移動します( cd を使用)
  • 最新の10個のログファイルを一覧表示します(lsおよび head を使用)
  • ログファイルのディスク使用量を印刷します( du を使用)
  • すべてのファイルシステムのディスク使用量を印刷します( df を使用)

それでも、“;“ 演算子を使用して、上記のすべてのタスクを1行に連結できます。

$ cd logs; ls -lt | head; du -sh ; df -h

ただし、次の点に注意する必要があります。 コマンドチェーンの1つのコマンドが失敗した場合 、 残りのコマンドはとにかく実行されます。 そして、これは予期しない出力を生成する可能性があります。 

4. コマンドを条件付きで連結する

上記のアプローチは、すべての状況に適しているわけではありません。 前のの成功または失敗に基づいてコマンドを実行するなど。 このような場合、次のいずれかの方法を使用できます。

4.1. コマンドを「 && 「「

&&」 また AND演算子は、前のコマンドが成功した場合にのみ2番目のコマンドを実行します。

サーバーのホームディレクトリにいるとしましょう。 特定のフォルダarchive_old、があります。このフォルダには、多くのディスク領域を消費する古くて不要なファイルがあります。 したがって、ディスク領域を解放するには、そのフォルダの内容を削除する必要があります。

そのために、次のコマンドを1つずつ実行できます。

$ cd archive_old; rm -rf *

フォルダ名が正しい場合、これは正常に機能します。 ただし、誤ってフォルダ名のつづりを間違えた場合、必要なディレクトリが存在しないと仮定します。 したがって、ホームディレクトリで作業を続けます。 すると、 cd コマンドが失敗しても、rmコマンドが実行されます。 その結果、ホームディレクトリのすべての内容が削除されます。 これは致命的です。

このような状況で、 「;」 && 」演算子:

$ cd archive_oldd && rm -rf *

このように、rmコマンドは実行されません。 これは、「 &&」 オペレーターは rm 次の場合にのみコマンド CD コマンドは成功します。

4.2. コマンドを“ ||”と連結します

“ ||”またはOR演算子は、先行するコマンドがエラーを返した場合にのみ2番目のコマンドを実行します。

ログファイルをアーカイブするための新しいシェルスクリプトを作成していると仮定しましょう。 新しいシェルスクリプトを実行する前に、それを実行可能にする必要があります。 “ ||”演算子を使用してこれを実現する方法を見てみましょう。

まず、実行可能かどうかを確認します。実行可能でない場合は、chmodコマンドを使用して実行可能にします。

$ [ -x "archive_logs.sh" ] || chmod +x archive_logs.sh

この例では、 chmod コマンドは、ファイルarchive_logs.shが実行可能でない場合にのみ実行されます。

5. グループコマンド

Bashでは、「 {} 」演算子と「(」演算子の両方を使用してコマンドをグループ化できます。 

このセクションでは、それらを使用してコマンドをグループ化し、例を通じてそれらの違いを理解する方法について説明します。 また、コマンドをグループ化する日常のユースケースについても説明します。

5.1. 「{}」を使用したコマンドのグループ化

コマンドをグループ化するために中括弧「{}」を使用する構文は次のとおりです。

{ command-list; }

コマンドリストに続くセミコロン(または改行)が必要であることに注意してください。

{}」を使用して4つのコマンドをグループ化する例を見てみましょう。

$ { 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. 「()」を使用したコマンドのグループ化

コマンドをグループ化するために括弧「()」を使用する構文は、コマンドリストに続くセミコロンがオプションであることを除いて、非常に似ています。

( command-list )

()」を使用して同じコマンドをグループ化しましょう。

$ ( 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. 「{}」と「()」の違い

{}」と「()」を使用したコマンドのグループ化には、1つの違いがあります。

  • {コマンド; } :コマンドは現在のシェルで実行されます
  • (コマンド):コマンドはサブシェルで実行されます

サブシェル内の変数を変更すると、変更はサブシェルの外部には表示されません。 2つのグループ化方法の違いを理解するための例を見てみましょう。

まず、「{}」を使用してコマンドをグループ化します。

$ VAR="1"; { VAR="2"; echo "Inside group: VAR=$VAR"; }; echo "Outside: VAR=$VAR"
Inside group: VAR=2
Outside: VAR=2

上記の出力が示すように、変数 VAR =” 1” を初期化し、グループ内でその値を“ 2”に変更しました。 変数はコマンドグループの外部でも更新されます。

次に、「()」を使用して同じテストを実行しましょう。

$ VAR="1"; ( VAR="2"; echo "Inside group: VAR=$VAR" ); echo "Outside: VAR=$VAR"
Inside group: VAR=2
Outside: VAR=1

今回は、変数がサブシェルで変更されます。 したがって、変更は外殻に影響を与えません。

cdコマンドの別の例を見てみましょう。

$ pwd; { cd /etc; pwd; }; pwd
/tmp/test/baeldung
/etc
/etc

$ pwd; ( cd /etc; pwd ); pwd
/tmp/test/baeldung
/etc
/tmp/test/baeldung

この例では、最初の出力は単純です。 しかし、2番目の出力で、「()」のディレクトリ変更が保持されなかったのはなぜですか。

これは、cdコマンドが環境変数$PWD を設定し、pwdコマンドが変数$PWDを読み取るためです。 cdコマンドがサブシェルの変数$PWD を更新した場合、その変更は外部シェルには表示されません。

5.4. コマンドをグループ化する必要があるのはいつですか?

主に、次の2つのシナリオでコマンドをグループ化する必要があります。

  • コマンドのリストに同じリダイレクトを適用する
  • コマンドのリストに論理演算子を適用する

コマンドがグループ化されると、リダイレクトがコマンドリスト全体に適用される場合があります。

$ ( 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

使用できることを学びました && || コマンドを条件付きで連結します。 コマンドグループを使用すると、条件が満たされたときにコマンドのリストを実行できます。

たとえば、Webサイトにpingを実行して、Webサイトが稼働しているかどうかを確認しようとします。 ping コマンドが失敗した場合にのみ、管理者にSMSを送信し、ログメッセージを書き込みます。 これを実現するために、sendSMSコマンドとwriteLogコマンドをグループ化できます。

$ ping -c1 "some.website" 1>/dev/null 2>&1 || ( sendSMS && writeLog )

実行をシミュレートするには、 echo コマンドを使用して、sendSMSおよびwriteLogコマンドをシミュレートし、常に正常に実行できるようにします。

$ alias sendSMS='echo "sending SMS..(site is down!)..DONE"'
$ alias writeLog='echo "writing logs..(site is down!)..DONE"'

次に、コマンドグループを使用していくつかのテストを実行しましょう。

$ 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 )

コマンドグループを使用すると、 ping コマンドが成功したか失敗したかに関係なく、コマンド全体が期待どおりに実行されます。

次に、sendSMSコマンドとwriteLogコマンドをグループ化しないとどうなるか見てみましょう。

$ 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

上記の出力が示すように、sendSMSコマンドとwriteLogコマンドをグループ化せずに、サイトがダウンしている場合、コマンド全体が期待どおりに機能します。

ただし、 ping コマンドが成功した場合は、とにかくwriteLogコマンドが実行されます。 これは私たちが望んでいることではありません。

したがって、コマンドをグループ化すると、いくつかの問題を適切に解決できる場合があります。

6. バックグラウンドでの複数のコマンド

バックグラウンドモードで単一のコマンドを実行するには、「 」演算子。 ただし、バックグラウンドで複数のコマンドを実行するには、次の2つの方法のいずれかを使用できます。

  • 使用する “ ” 一緒に “ && 「「
  • 使用する “ 」とコマンドグループ

アプリケーションデータベースをバックアップするシェルスクリプトがあるとしましょう。それをexecute_backup_db.shと呼びましょう。 このシェルスクリプトが完了するまでにかかる時間を確認する必要があります。 シェルスクリプトの実行前後にdateコマンドを使用して、合計実行時間を取得してみましょう。 このシェルスクリプトは、完了するまでに長い時間がかかる場合があります。

では、 「&」演算子を使用してバックグラウンドでこれを実行しますコマンド出力をログファイルに記録します。

$ date; ./execute_backup_db.sh; date & > execute_backup_db.log

使用する 「&」 と “;” バックグラウンドでのみ最終日付コマンドを実行します 。 ただし、最初の日付コマンドとシェルスクリプトはフォアグラウンドでのみ実行されます。 代わりに、すべてのコマンドをバックグラウンドで条件付きで実行するには、次のようにします。

$ date && ./execute_backup_db.sh && date & > execute_backup_db.log

;」で同じことを実現するには、「()」演算子を使用します。

$ (date ; ./execute_backup_db.sh ; date) & > execute_backup_db.log

7. 結論

このチュートリアルでは、コマンドラインで複数のコマンドを効率的に組み合わせて実行できるさまざまな方法を見てきました。

複数のコマンドの実行が無条件の場合は、演算子“;“を使用できます。 一方、オペレーターは「 && ” と “ || 」は、コマンドの実行が前のコマンドの成功または失敗に基づいている場合に使用できます。

さらに、コマンドをグループ化する2つの方法を取り上げ、それらの違いについて説明しました。

また、フォアグラウンドとバックグラウンドで複数のコマンドを実行する方法も確認しました。

ただし、複数のコマンドを特定の順序で定期的に実行するには、それらすべてのコマンドをシェルスクリプトに配置する必要がある場合があります。