2017-02-23 6 views
0

私は、シェルスクリプトの下に、私はgit diffgit addできない

を行うにはアマチュアのシェルスクリプトのコーダしようとしていますが、私は$agit diff $aない印刷することはできませんよ期待どおりに動作します。

#!/bin/sh 

GITSTATUS=($(git status)) 

for x in ${!GITSTATUS[@]}; do 
     if [[ "${GITSTATUS[$x]}" == *"modified"* ]]; then 
       a=`echo ${GITSTATUS[$x]} | sed 's/modified://'` 
       echo "Modified file :$a" ## <-- Does not show filename 
       git diff $a    ## <-- Does not operate correctly 
       echo "Do you want to add this file: (Y/n)" 
     fi 
done 

変数をコマンドとともに使用する方法を教えてください。

+0

あなたは '/ bin/sh'で配列を使用しようとしています。これはPOSIX shで利用可能な機能ではありません。代わりにkshまたはbashを使用する必要があります。 –

+0

ところで、強調表示/書式設定のためにコード内に '**'などを使用しないでください - 現在の言語に適したコメント文字を使用してください。 –

+0

...それを超えて、 'array =($(somecommand))'はそれをサポートしているシェルでさえ悪い習慣です。文字列分割だけでなく、somecommand '。代わりにIFS上で単語を分割するが、グロブを展開しない* read -r -a array <<(somecommand)を使用してください。実際には配列の有効なユースケースが存在するとは限りません。 –

答えて

2

3点:

  1. このスクリプトは、#!/bin/shシェバングとのkshやbashでのみ利用できる多くの機能を使用しようとしています。 /bin/shbashへのリンクであっても、その名前で呼び出されるとPOSIX互換モードで動作します。しかし、/bin/shがbashになるという保証はありません。多くのシステムはashまたはdashです。 Choose Your Shellを参照してください。

  2. はでもbashの上、ごforループが言葉、ないラインの繰り返し処理を行うことになります。その結果、${GITSTATUS[$x]}modified:の場合、という語句modified:とファイル名は含まれません。 Don't Read Lines With Forを参照してください。

  3. --porcelainオプションなしgit statusの出力を解析しようとしないでください:デフォルトの出力は、人間の消費ではなく、スクリプトのためのものであり、将来的には予測できない方法で変更することができます(またはベースの出力の変化を受けることができます現在の言語またはロケールで)。

    --porcelain[=<version>] - Give the output in an easy-to-parse format for scripts. This is similar to the short output, but will remain stable across Git versions and regardless of user configuration. See below for details.


#!/bin/bash 
topdir=$(git rev-parse --show-toplevel) || exit # find repository root 
cd "$topdir" || exit        # ...and cd to it 
while IFS= read -r -d '' line <&3; do    # read NUL-delimited lines 
    [[ ${line:3:1} = " " ]] || continue    # sanity-check: format is "XY filename" 
    x=${line:0:1}         # X: index status 
    y=${line:1:1}         # Y: working-tree status 
    case $x$y in          # compare *both* flags: if EITHER is... 
    *[CR]*) IFS= read -r -d '' _ <&3 ;;   # ...copy/rename: skip dest name 
    esac 
    case $y in          # Compare *working-tree* flag: 
    *M*) : ;;         # ...modify: continue loop 
    *)  continue ;;       # ...otherwise: skip to next filename 
    esac 
    filename=${line:3}        # extract filename from line 
    printf 'Modified file: %q\n' "$filename" >&2  # print (stderr for user interaction) 
    git diff -- "$filename"       # run git diff 
    while :; do          # loop for a valid response 
    echo "Do you want to add this file? (Y/n)" >&2 # prompt the user 
    IFS= read -r answer || break     # ...abort if read fails 
    case $answer in 
     [Yy]*|"") git add -- "$filename"; break;; 
     [Nn]*) break;; 
     *)  echo "Response not recognized" >&2;; 
    esac 
    done 
done 3< <(git status --porcelain -z) 

いくつかのキーノート:

  • 我々はリポジトリのトップへcdがいることを確認することです理由git statusのmanページから

    に含まれるパスは、git diffの必要に応じて、現在のディレクトリに対して有効です。

  • ${line:0:1}および${line:1:1}は、1番目と2番目の文字を選択するパラメータ展開演算子です。 the bash-hackers wiki page on parameter expansionを参照してください。
  • このコード内のすべての拡張は、"$filename"ではなく、$filenameではありません。これは、名前にスペースまたはグロブ文字を含むファイルを正しく処理するために不可欠です。
  • git statusに引数-zを使用すると、出力がNULで区切られ、フォーマットが完全に明確になります(NULはファイル名に存在しない文字です)。
  • ファイルディスクリプタ3を使用してgit statusから読み取ると、ファイルディスクリプタ0(stdin)が端末に接続されたままになり、プロンプトが表示されます。
+2

奇妙なことに、配列は 'bash'が' sh'として呼び出されたとしても、まだ動作するものの1つです。 – chepner

+0

強調した元のコメントを編集しました。 – chepner

+0

@Charles下にエラーが表示されています。オプション 'porcelain 'は値なし – Elankeeran