Bashでファイルシステムにまだ存在するコミットされたファイル配列をどのように生成しますか?

Bashでファイルシステムにまだ存在するコミットされたファイル配列をどのように生成しますか?

私はbashスクリプトを使ってGitフックを生成しています。送信したファイルに対してプロセス(リンティング)を実行したいと思います。ファイルが実際に存在する場合にのみプロセスを実行したいと思います。つまり、コミットから削除されたファイルは考慮されません。私はこれを試しました

    CHANGED_FILES=$(git diff --cached --name-only | grep ".js$")
    echo "$CHANGED_FILES"
    
    if [ -n "$CHANGED_FILES" ]; then
      REAL_FILES=()
      for pathname in "${CHANGED_FILES[@]}"; do
        if [ -e "$pathname" ]; then
            REAL_FILES+=( "$pathname" )
        fi
      done

  echo "real files are ..."
  echo "$REAL_FILES"

ただし、上記の方法は単一のファイルが変更された場合にのみ機能します。複数のファイルが変更されても何も起こらず、私の出力は次のようになります。

    src/app/test1.js
    src/app/test2.js
    real files are ...

したがって、CHANGED_FILES配列に実際に存在するファイルが2つ以上含まれている場合、「REAL_FILES」については何も保存されません。

上記の問題をどのように解決できますか?

ベストアンサー1

スクリプトの問題は、CHANGED_FILESが配列ではなくスカラー変数であることです。

努力する:

CHANGED_FILES=( $(git diff --cached --name-only | grep ".js$") )

つまり、コマンド置換の周りに括弧を追加します$(git...)

これは一般的なシェルワード分割の影響を受けるため、パス名にスペース/改行/シェルメタ文字がある可能性(*)がある場合は、以下を使用する必要がありますmapfileプロセスの交換変えるコマンドの置き換え、オプションで、NUL区切り記号の両方を使用します-z。たとえば、git diffgrep

mapfile -d '' -t CHANGED_FILES < \
  <(git diff -z --cached --name-only | grep -z '\.js$')

ところで、mapfileそしてreadarrayはbashの同義語です。詳しくはこちらをご覧くださいhelp mapfile。にのみksh存在しませreadarraymapfile

(*) 持ついつもこれらの文字はUNIXファイルシステムで有効なファイル名文字であるため、これを行う可能性があります。パス名の唯一の無効な文字はNULです(パス名のファイル名部分の唯一の無効な文字はNUL/です)。したがって、どこかにそのような不愉快なファイル名が常に1つ以上あると仮定し、常に防御的にスクリプトを作成する必要があります。つまり、このmapfileバージョンは常に使用されます。


ちなみに、$REAL_FILES配列のすべての要素(最初の要素だけでなく)を印刷するには、下付き[@]文字を使用するかdeclare -p/を使用しますtypeset -p(これはデバッグに適しています。 )。出力)空の質問)。

たとえば、次のようになります。

echo "$REAL_FILES"

これを行う:

echo "${REAL_FILES[@]}"

またはこれ:

typeset -p REAL_FILES

declarevsはtypeset別の違いですbash。kshkshにはreadarraybashがあり、typesetbashはkshからコピーして名前を変更し、mapfilekshdeclareのバージョンをエイリアスとして追加します。理由はわかりません)

おすすめ記事