3

bashスクリプトを使用してgitとのやり取りの一部を自動化しようとしています。そのために、すべてのリポジトリをsshして一覧表示し、それらのクローンを作成するスクリプトがあります。スクリプトの一部をここに示します

DIRS=`ssh $u "ls $p"`;
for DIR in $DIRS; do
    echo "ssh://$u$p$DIR"
    echo "git clone ssh://$u$p$DIR";  
    git clone ssh://$u$p$DIR
done

引数の例は

-u user@host

-p /cygdrive/c/Documents\ and\ Settings/somePath

これは、名前にスペースが含まれているファイルパスで、スペースが多すぎるというエラーをスローするgitclone部分までは正常に機能します。エコーした部分をコピー&ペーストすると

echo "git clone ssh://$u$p$DIR";

その後、それはうまくクローンします。これを引用符で囲もうとしましたが、エラーが発生します

does not appear to be a git repository

私は何が間違っているのですか?

編集1

使用時のエラーgit clone "ssh://$u$p$DIR"

fatal: '/cygdrive/c/Documents\ and\ Settings/somePath/repo1' does not appear to be a git repository
fatal: The remote end hung up unexpectedly
4

5 に答える 5

3

ディレクトリ名にスペースが含まれている場合は、これを実行したくないことは間違いありません。

DIRS=`ssh $u "ls $p"`;
for DIR in $DIRS; do

スペースを区切り文字として使用するのではなく保存したいので、これを行う一般的に受け入れられている方法は、区切り文字が改行であることをbashに伝えることです。

IFS=$'\n'
for DIR in `ssh $u "ls -1 $p"`; do

本当に変数が必要な場合はDIRS、bashの配列変数を使用できます(スペースを保持します)。

IFS=$'\n'
DIRS=(`ssh $u "ls -1 $p"`)
unset IFS
for DIR in "${DIRS[@]}"; do

別の一般的な形式は次を使用していreadます:

ssh $u "ls -1 $p" | while read DIR; do

または:

while read DIR; do
    ...
done < <(ssh $u "ls -1 $p")

ただし、これらはエラー処理としてはお勧めしません(特に、sshパイプを使い始めると、少し複雑になります)。

これまでのすべては、基本的なbashスクリプトです。

この行については、2番目の引数を二重引用符で囲む必要があります。これは、のスペースによって$DIR3番目の引数が作成されるためです。

git clone "ssh://$u$p$DIR"

私はこれをgitで試しましたが、うまくいくようです。ただし、@ holygeekが言ったように、スペースをに置き換えること%20もうまくいくようで、場合によっては有益かもしれません。あなたはそのようにこれを行うことができます:

git clone "ssh://$u$p${DIR// /%20}"
于 2012-09-10T01:10:12.557 に答える
1

あなたの問題はほぼ間違いなくファイル名のスペースです

findスペースの代わりに(ヌル)文字xargsを使用してファイル名を区切る特別なモードをサポートできます。\0

これを試して:

ssh $u find $p -depth 0 -print0 | xargs -0 -J % git clone ssh://$u%

を実行することで、例のようなスクリプトを簡単にデバッグできます。

bash -x myscript.sh

次に、実行する前に各コマンドラインを出力します。これにより、問題が発生する前に実際に実行されているものを見つけることができます。

于 2012-09-09T19:44:58.963 に答える
1

2つの問題があります。1つは、lsファイル名に対してあらゆる種類の奇妙なことを行うを使用したことです。の出力を解析しないでくださいlsもう1つは、を二重引用符で囲んでいないことです$u$p$DIRコマンド置換の前後には常に二重引用符を付けてください(変数の値が空白で区切られたグロブパターンのリストである場合を除きます。これはめったにありません)。

$p一般に、引用符も付ける必要があることに注意してください。sshを通過しているため、反対側で解釈されることは避けられません。スペースなどのシェル特殊文字が含まれていない場合$p、これはそれほど難しくありません。もしそうなら、それらを保護する最も簡単な方法は、その$p周りに一重引用符を付けてローカル側で拡張することです。一重引用符は、一重引用符が含まれている場合を除いて、リモートシェルの文字列リテラルを形成し$pます。これらはシーケンスに置き換える必要があります'\''。構成に対するBashの解析ルールは${p//PATTERN/REPLACEMENT}少し奇妙です。中間変数を使用した2段階のプロセスがその役割を果たします$q

改行文字を含まないファイル名に依存できる場合は、リモート側で1行に1つのファイル名を印刷できます。

q=\'\\\'\'
ssh "$u" "cd '${p//\'/$q}' && for x in *; do echo \"\$x\"; done" | {
  p=${p%/}
  p=${p//%/%3f}
  case $p in
    /*) :;;
    *) p="/~/$p";;
  esac
  while IFS= read -r DIR; do
    DIR=${DIR//%/%3f}
    git clone "ssh://$u$p/$DIR"
  done
}

の一部の特殊文字、$pまたは$DIRgitURLでさらに引用符を付ける必要があることに注意してくださいssh:。スペースは問題ありませんが%、少なくともにエスケープする必要があります%3f$p上記のコードでは、そのサポートと、が相対パス(ホームディレクトリに対して相対パスとして解釈される)の場合のサポートを追加しました。

于 2012-09-09T23:41:38.373 に答える
0

GillesAntakの回答からの情報を使用して、解決策を見つけました。また、リモートマシンでsshを実行し、bashヒアドキュメントで変数を使用することに関するその他の投稿もいくつかあります。

Gillesが提案したように、私は使用を避け、スペースからの複数の引数を避け、改行で引数を分割するために使用するlsというAntakの提案を使用しました。IFS=$'\n'

私が決めた最後のコードは

DIRS=`ssh "$u" << ENDSSH
    cd "$p"
    eval 'for file in *; do echo \\$file; done'
ENDSSH`

IFS=$'\n'
for DIR in $DIRS;
    do
    echo "ssh://$u$p/$DIR"
done
unset IFS 
于 2012-09-10T16:41:32.853 に答える
-2

スペースを%20に置き換えることが効果的かどうかを確認してください。

于 2012-09-09T22:34:09.097 に答える