ssh se rompe de while-loop en bash

Utilizo este código bash para cargar archivos en un servidor remoto, para archivos normales esto funciona bien:

for i in `find devel/ -newer $UPLOAD_FILE` do echo "Upload:" $i if [ -d $i ] then echo "Creating directory" $i ssh $USER@$SERVER "cd ${REMOTE_PATH}; mkdir -p $i" continue fi if scp -Cp $i $USER@$SERVER:$REMOTE_PATH/$i then echo "$i OK" else echo "$i NOK" rm ${UPLOAD_FILE}_tmp fi done 

El único problema es que para los archivos con un espacio en el nombre, el for-loop falla, así que reemplacé la primera línea así:

 find devel/ -newer $UPLOAD_FILE | while read i do echo "Upload:" $i if [ -d $i ] then echo "Creating directory" $i ssh $USER@$SERVER "cd ${REMOTE_PATH}; mkdir -p $i" continue fi if scp -Cp $i $USER@$SERVER:$REMOTE_PATH/$i then echo "$i OK" else echo "$i NOK" rm ${UPLOAD_FILE}_tmp fi done 

Por alguna extraña razón, el comando ssh se rompe del ciclo while, por lo tanto, el primer directorio que falta se crea correctamente, pero se ignoran todos los archivos / directorios que faltan.

Supongo que esto tiene algo que ver con que ssh escriba algo a stdout que confunde el comando “leer”. Comentando el comando ssh hace que el bucle funcione como debería.

¿Alguien sabe por qué sucede esto y cómo uno puede evitar que ssh rompa el ciclo while?

El problema es que ssh lee desde la entrada estándar, por lo tanto, come todas las líneas restantes. Simplemente puede conectar su entrada estándar a ninguna parte:

 ssh $USER@$SERVER "cd ${REMOTE_PATH}; mkdir -p $i" < /dev/null 

También puede usar ssh -n lugar de la redirección.

Otro enfoque es pasar por encima de un FD distinto de stdin:

 while IFS= read -u 3 -r -d '' filename; do if [[ -d $filename ]]; then printf -v cmd_str 'cd %q; mkdir -p %q' "$REMOTE_PATH" "$filename" ssh "$USER@$SERVER" "$cmd_str" else printf -v remote_path_str '%q@%q:%q/%q' "$USER" "$SERVER" "$REMOTE_PATH" "$filename" scp -Cp "$filename" "$remote_path_str" fi done 3< <(find devel/ -newer "$UPLOAD_FILE" -print0) 

Los operadores -u 3 y 3< son críticos aquí, usando FD 3 en lugar del FD 0 predeterminado (stdin).

El enfoque que se da aquí -utilizando -print0 , un valor IFS despejado, y similares- también es menos problemático que el código original y la respuesta existente, que no puede manejar nombres de archivo interesantes correctamente. (La respuesta de Glenn Jackman está cerca, pero incluso eso no puede tratarse con nombres de archivos con líneas nuevas o nombres de archivos con espacios en blanco al final).

El uso de printf %q es crítico para generar comandos que no pueden usarse para atacar la máquina remota. Considere lo que sucedería con un archivo llamado devel/$(rm -rf /)/hello con código que no tenía esta paranoia.

Además de la respuesta de choroba , no use un bucle for para leer nombres de archivos:

 find devel/ -newer $UPLOAD_FILE | while read -ri do ...