
私は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 diff
grep
mapfile -d '' -t CHANGED_FILES < \
<(git diff -z --cached --name-only | grep -z '\.js$')
ところで、mapfile
そしてreadarray
はbashの同義語です。詳しくはこちらをご覧くださいhelp mapfile
。にのみksh
存在しませreadarray
んmapfile
。
(*) 持ついつもこれらの文字はUNIXファイルシステムで有効なファイル名文字であるため、これを行う可能性があります。パス名の唯一の無効な文字はNULです(パス名のファイル名部分の唯一の無効な文字はNUL/
です)。したがって、どこかにそのような不愉快なファイル名が常に1つ以上あると仮定し、常に防御的にスクリプトを作成する必要があります。つまり、このmapfile
バージョンは常に使用されます。
ちなみに、$REAL_FILES配列のすべての要素(最初の要素だけでなく)を印刷するには、下付き[@]
文字を使用するかdeclare -p
/を使用しますtypeset -p
(これはデバッグに適しています。 )。出力)空の質問)。
たとえば、次のようになります。
echo "$REAL_FILES"
これを行う:
echo "${REAL_FILES[@]}"
またはこれ:
typeset -p REAL_FILES
(declare
vsはtypeset
別の違いですbash
。kshksh
にはreadarray
bashがあり、typeset
bashはkshからコピーして名前を変更し、mapfile
kshdeclare
のバージョンをエイリアスとして追加します。理由はわかりません)
答え2
echo "$REAL_FILES"
配列を文字列に連結する必要はありません。
echo "${REAL_FILES[*]}"
または、各要素を印刷できます。
printf "%s\n" "${REAL_FILES[@]}"