2012-02-22 14 views
51

que utilizan este bash-código para cargar archivos en un servidor remoto, para los archivos normales esto funciona bien:rompe ssh fuera del bucle while en bash

for i in `find devel/ -newer $UPLOAD_FILE` 
do 
    echo "Upload:" $i 
    if [ -d $i ] 
    then 
     echo "Creating directory" $i 
     ssh [email protected]$SERVER "cd ${REMOTE_PATH}; mkdir -p $i" 
     continue 
    fi 
    if scp -Cp $i [email protected]$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 bucle de falla, así que sustituyó a la primera línea como la siguiente:

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

por alguna extraña razón, los saltos de ssh-comando fuera del bucle while, por lo tanto, se crea el primer directorio que falta bien, pero todos los archivos/directorios faltantes subsiguientes son ignorados.

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?

+0

Como un lado, todos los nombres de variables mayúsculas están reservados por convención para las variables de entorno y builtins; no deberían usarse para variables locales a un script. –

+0

'while read' se romperá mal cuando sus nombres de archivo contengan barras invertidas literales. Más seguro para usar 'read -r'. Del mismo modo, 'leer' quitará los espacios en blanco de los nombres; para evitar eso, debes borrar 'IFS'. –

+0

Además, 'encontrar | while read' emite una secuencia delimitada por una nueva línea, pero los nombres de archivo legítimos en UNIX pueden contener líneas nuevas. Piense en lo que sucede si alguien hace un 'mkdir -p devel/$ '\ n'/etc' y luego escribe en' devel/$ '\ n'/etc/passwd'; será mejor que espere en este momento que su script no tenga permiso para escribir en '/ etc/passwd' en la máquina remota. –

Respuesta

124

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

ssh [email protected]$SERVER "cd ${REMOTE_PATH}; mkdir -p $i" < /dev/null 

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

+2

¡Gracias, eso fue todo! – Robby75

+32

También puede usar ssh -n para lograr lo mismo. – jordanm

+1

Esto es bastante problemático, por cierto. ¿Qué pasa si '$ 1' es un nombre de archivo con espacios? Terminas creando más de un directorio. Del mismo modo para '$ REMOTE_PATH'. 'printf% q' se puede usar para citar variables que se pasan con seguridad a través del shell extra que invoca ssh. –

3

Además de choroba's answer, no utiliza un bucle for para leer los nombres de archivo:

find devel/ -newer $UPLOAD_FILE | 
while read -r i 
do ... 
5

Otro enfoque consiste en bucle sobre un FD distinta de la entrada estándar:

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 "[email protected]$SERVER" "$cmd_str" 
    else 
    printf -v remote_path_str '%[email protected]%q:%q/%q' "$USER" "$SERVER" "$REMOTE_PATH" "$filename" 
    scp -Cp "$filename" "$remote_path_str" 
    fi 
done 3< <(find devel/ -newer "$UPLOAD_FILE" -print0) 

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

El enfoque dado aquí - usando -print0, un valor IFS despejado, y similares - es también menos buggy de que el código original y la respuesta existente, que no puede manejar nombres de archivos 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 se pueden usar para atacar la máquina remota. Considere lo que sucedería con un archivo llamado devel/$(rm -rf /)/hello con un código que no tenía esta paranoia.

Cuestiones relacionadas