web-dev-qa-db-ja.com

Bashスクリプトでコマンドを1行に結合するのはなぜですか?

LinuxとBashスクリプトは初めてです。職場では、次のような構成のBashスクリプトを見てきました。

mkdir build && cd build && touch blank.txt

または:

mkdir build; cd build; touch blank.txt

またはエキゾチックです:

COMMAND="mkdir build && cd build && touch blank.txt"
eval ${COMMAND}

最後の例は、単一の行が役立つ可能性のある1つの可能なユースケースを示していますが、一般的には次の方が読みやすく、(少なくとも私にとって)スクリプトを視覚的にデバッグできます。

mkdir build
cd build
touch blank.txt

すべてを1つの行に詰め込むことには技術的な利点がありますか?

36
AlainD
mkdir build && cd build && touch blank.txt

Bash(および他のいくつかのシェル)では、&&は論理およびです。つまり、前のコマンドがtrueを返した場合、次のコマンドは実行されました。論理または||もあります。たとえば、次の2つのオプションを1つのステートメントで組み合わせることができます。

mkdir /tmp/abc/123 && echo 'the dir is created' || echo "the dir isn't created"

cmd_1 && cmd_2 || comd_3構文はif; then; elseステートメントの代用ではないことに注意してください。これは、前述のコマンドのどれがfalseを返しても、cmd_3が実行されるためです。したがって、使用する状況に注意する必要があります。次に例を示します。

$ true && false || echo success
success
$ false && true || echo success
success
$ false && false || echo success
success

経験則として、通常、スクリプト内でcdコマンドを使用している場合、ディレクトリの変更が成功したかどうかをテストしています:cd somewhere/ || exit。より適切なテストは @ dessertによって提案されたif ! cd dir; then exit 1; fiです。ただし、すべての場合において、スクリプトの失敗の保護として、_ @ mrks 'answer に示されているように、set -eオプションを使用することをお勧めします。


mkdir build; cd build; touch blank.txt

;は行区切り文字で、1行にいくつかの個別のコマンドが記述されている場合に使用されます。


;または&&||を使用している場合は、コマンドをオンラインで書き込む必要はありません。これは @ allo's answer に示されています。

IMOでは、コマンドを1行で記述するか、別の行で記述するかについて、特別な技術的な利点や違いはありません。


COMMAND="mkdir build && cd build && touch blank.txt"
eval ${COMMAND}

ここでは、1つ以上のコマンド(およびそれらの引数)が変数の値としてグループ化され、この変数(引数)はevalを使用してコマンドに変換されます。したがって、1つの場所でのみ変更することにより、スクリプト内で複数回実行されるコマンドを変更できます。

たとえば、blank.txtファイルの作成方法を変更する必要があるとします。たとえば、次のように関連する行を変更できます。

COMMAND="mkdir build && cd build && echo 'test' > blank.txt"

evalの実際のaliasの-​​ advantage は、リダイレクト、パイプ、論理演算子などが使用されている場合です。 functionsが適用されない場合、ほとんどの場合、evalの代わりにaliasを使用できます。また、変数にコマンドが1つだけ含まれている場合、つまりCMD="cat"の場合、evalは必要ありません。Bashworld-splitは"$CMD" "$file1" "$file2"を正しく展開するためです。

このセクションの以前のバージョンは、コメント内で説明されています ここ

53
pa4080

これまでの答えは何が起こるか/どのように機能するかを説明していますが、あなたは「なぜ」と尋ねていたと思います

3行で入力するのではなく(私にとって)それを行う主な「理由」は、コマンドが時々(場合によっては数分)かかることがあり、ずっとハングアウトしたくないということです。

たとえば、&&デプロイ&&をビルドしてアプリを起動し、昼食に出かけることができます。プロセスが完了するまで15分かかる場合があります。ただし、&&を使用したため、ビルドが失敗した場合はデプロイされないため、機能します。

先行入力は代替手段ですが、それは不鮮明です。入力内容を確認できず(したがって、間違いを犯す)、プログラムが先行入力を使用する可能性があります(ありがとう、Grails!)。次に、昼食から戻って、タイプミスのためにキックオフする2つの長いタスクがあることに気付きます。

もう1つの方法は、小さなスクリプトまたはエイリアスを作成することです。悪い考えではありませんが、次のような柔軟性はありません。

stop && build && test && deploy && start 

または私は次のことができます:

stop && start

または、フラグの束でスクリプトをすばやく汚染する他の組み合わせの数。

スクリプトを記述したとしても、&&を使用して他のスクリプトと統合する可能性があります。

13
Bill K

Linuxでコマンドを組み合わせると非常に便利です。

良い例は、sshを介してリモートネットワークインターフェースを再起動することです(ネットワーク構成などを変更した場合)。

ifdown eth0 && ifup eth0

これにより、元々sshを実行していたインターフェースを起動するために、サーバーに物理的にアクセスすることができなくなります。 (実行できなくなりますifup eth0の場合ifdown eth0は単独で実行されます。)

10
pymym213

コマンドは何をしますか?

その理由を理解するために、何が行われているかを理解する必要もあります。スクリプトは順次です。あなたの最後の例では:

mkdir build
cd build
touch blank.txt

cd buildは、mkdir buildが成功したかどうかに関係なく実行されます。もちろん、mkdir failsの場合、cdからのエラーが表示されます。

mkdir build; cd build; touch blank.txt順次リスト です。これは、基本的に複数のスクリプト行と同じと考えることができます。シェル文法はこれをわずかに異なる方法で扱いますが、結果は上記とまったく同じになります。ここでも、前のコマンドが成功したかどうかに関係なく、コマンドが実行されます。

最後に、

mkdir build && cd build && touch blank.txt

これは ANDリスト -&&演算子で区切られた1つ以上のパイプライン(またはコマンド)のシーケンスです。このようなリスト内のコマンドは、左結合で実行されます。つまり、シェルは&&で区切られた2つのコマンド/パイプラインを引き続き使用し、最初に左側で実行し、左側で成功した場合にのみ右側で実行します(ゼロの終了ステータスを返します)。

この例では、何が起こりますか?

  • シェルは最初にmkdir buildを実行します。
  • 上記のコマンドは成功しました(終了コード0を返しました)、cd buildが実行されます
  • 再度、左の結合性を見てみましょう。 ... && touch blank.txt&&の左側にあるものは成功しましたか?はいの場合は、touch blank.txtを実行します。

順次リストとANDリストを使用する理由

順次リストmkdir build; cd build; touch blank.txtは、コマンドの1つが失敗しても問題がない場合に意味があります。 mkdir buildがすでに存在するとします。それでもcdbuild/ディレクトリとtouch blank.txtに入れたいと思っています。もちろん、ここでの欠点は、意図しない結果が生じる可能性があることです。 buildに実行ビットが設定されていない場合、どうしてcd buildは失敗しますか? touch blank.txtは、意図したbuild/ではなく、現在の作業ディレクトリで発生します。

mkdir build && cd build && touch blank.txtについて考えてみましょう。これは多少論理的ですが、独自の落とし穴があります。 buildがすでに存在する場合は、おそらく誰かがtouch blank.txtもすでに実行している可能性があります。これにより、touch blank.txtを再度実行したくない場合があります。これにより、アクセスタイムスタンプが変更されるためです。望ましくないかもしれません。

結論

コマンドを同じ行に配置することは、コマンドの目的と達成しようとしていることに依存します。シェルはコマンドが完了するまでプロンプトを再表示しないため、順次リストを使用すると出力を簡略化できます。 ANDリストは、コマンドの条件付き実行を可能にし、不要なコマンドの実行を防ぐ場合があります。結論として、ユーザーはタスクの違いと適切なツールとして何を選択すべきかを知る必要があります。

7

もう1つの理由は、スクリプトの作成方法にある可能性があります。テクニカルユーザーが非常に長いインタラクティブコマンドラインを作成し、期待どおりに機能するまで繰り返し拡張およびテストし、結果をテキストファイルに貼り付け、#!/bin/bashを平手打ちすることは珍しいことではありません。その上にヘッダー。スクリプトの複数のセグメントがその方法によって進化する場合があります。

for i ingrep | cut | sed | xargsチェーンで始まる長い1行の構成、&&および||$(cat something)構成の頻繁な使用は、多くの場合、このような起源を示しています。

5
rackandboneman

なぜそれを使いたいのか

ここでAsk Ubuntで回答を書いているとき、人々が簡単に切り取って端末に貼り付けることができるように、それらを1行に入れると役立ちます。例えば:

$ CurrDir=$PWD ; cd /bin ; ls -la dd ; cd "$CurrDir"

-rwxr-xr-x 1 root root 72632 Mar  2  2017 dd

前に述べたように、同じ効果を得るために&&を使用することもできますが、上記のエラーが発生した場合、誤ったディレクトリになってしまう可能性があります。この場合、;の方が&&よりもテストされていません。

rick@alien:~/askubuntu$ CurrDir=$PWD && cd /bin && llllssss -la dd && cd "$CurrDir"

llllssss: command not found

rick@alien:/bin$ 

なぜそれを使わなければならないのか

複数の行をつなぎ合わせる必要があるコマンドがいくつかあります。 1つのケースは、省略されたif- commands -fiであり、1行である必要があります。例えば:

$ [[ 2 -eq 2 ]] && { echo 2 equals 2 ; echo setting three to 3 ; three=3 ; }

2 equals 2
setting three to 3
4

すべてを1つの行に詰め込むことには技術的な利点がありますか?

それはスタイルの問題だけです。技術的な利点はありません。

他の人が3行の違いをかなりよく説明していると思いますが、これは1行に入れる必要があるという意味ではないことに注意してください。

foo &&
bar

foo && barと同じです。あなたはまだ注意する必要があります、

foo
&& bar

同じではありませんが、fooを実行してから構文エラーを生成します。ほとんどの場合の一般的なルール:シェルが構文から、行が完全でないことを知っている場合、次の行に進むことができます。
最初の行をそのまま実行できる場合、それが実行されます(次の行は、最初の行がないため、構文エラーである場合とそうでない場合があります)。

スクリプトを読みやすくしたい場合は、次のように改行文字をエスケープできます

foo \
&& bar

改行文字の直前にある必要があるため、\文字の後に空白がないことを確認してください。

3
allo

ここでの回答の多くは安全性について述べています。たとえば、「cdが失敗した場合はどうなりますか」

cd foo && rm *などのコマンドを組み合わせることでその問題は解決しますが、それを行うにははるかに良い方法があります。

このフォルダ構造をとります:

.
./foo
./foo/bar
./foo2
./foo2/bar2
./test.sh
./do_not_delete_me

そして次のスクリプト:

find .
cd notexisting
rm *
cd ../foo2
rm *
cd ..
find .

notexistingが存在しないため、rm *.で実行され、do_not_delete_meを削除します。

ただし、スクリプトをset -eで開始すると、最初の失敗したコマンドで中止されます。

./test.sh: line 4: cd: notexisting: No such file or directory

コメントで述べたように、それをシバンに含めることは理にかなっています:#!/bin/bash -e

2
mrks