Concatenación de cadenas bash utilizada para crear la lista de parámetros

12

Dado este pedazo de fiesta:

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo $PARMS
rsync ${PARMS} . ${TARGET}

El eco muestra la cadena PARMS como se esperaba, no se muestra ningún error, pero rsync actúa silenciosamente como si las opciones agregadas por + = no existieran. Sin embargo, esto funciona como se esperaba:

PARMS='-rvu'
rsync ${PARMS} --delete --exclude='.git' . ${TARGET}

Supongo que arruiné algo con citas bash (siempre tuve problemas con eso), pero no estoy seguro de qué y por qué se ignoran las opciones a pesar de que la cadena parece haber sido construida correctamente.

neuviemeporte
fuente
1
echo "$PARMS"y rsync "${PARMS}"...
jasonwryan
Esto funciona para mí con la bashversión 4.2.25 sin ningún cambio.
Anthon

Respuestas:

17

Hay una diferencia entre:

PARMS+="... --exclude='.git'"

y

... --exclude='.git'

En el primero, las comillas simples son comillas internas, por lo que están literalmente presentes en el texto sustituido dado rsynccomo argumentos. rsyncobtiene un argumento cuyo valor es --exclude='.git'. En el segundo, las comillas simples son interpretadas por el shell en el momento en que se escriben, porque no están dentro de las comillas y rsyncpueden ver --exclude=.git.

En este caso, no necesita las comillas simples: .gites una palabra de shell perfectamente válida por sí sola, sin caracteres especiales, por lo que puede usarla literalmente en el comando.

Sin embargo, mejor para este tipo de cosas es una matriz :

PARMS=(-rvu)
PARMS+=(--delete --exclude='.git')
rsync "${PARMS[@]}"

Esto construye su comando como palabras separadas, con cualquier cita que desee interpretada en el momento en que escribe la línea de matriz. "${PARMS[@]}"se expande a cada entrada de la matriz como un argumento separado, incluso si el argumento en sí tiene caracteres especiales o espacios, por rsynclo que ve lo que escribió tal como lo dijo.

Michael Homer
fuente
bashrealizó la división de palabras después de que ${PARMS}se expandió. Entonces la cita simple también fue interpretada por el shell.
Cuonglm
2
¡Intentalo! Yo hice. Las comillas permanecen, y si hubiera espacios intermedios, de todos modos son puntos divididos.
Michael Homer
@Gnouc: Desde la página de manual de bash: "La eliminación Cita: Después de las expansiones anteriores, todas las apariciones de los personajes no cotizados \ , 'y ". Que no resultó de una de las expansiones anteriores se eliminan" "expansiones anteriores" incluye la expansión de parámetros que realiza la expansión de ${PARMS}.
camh
Gracias. Entonces, entiendo que en ese caso, omitir las comillas simples dentro de las dobles funcionará, pero por completo, ¿qué pasa si necesito citar algunos caracteres especiales y no quiero usar su último enfoque?
neuviemeporte
Si sus caracteres especiales no son parte de IFS(generalmente, espacios en blanco), no necesita citarlos. Si lo están, no tienes suerte a menos que hackees algo con él eval, esto es un poco un error en general, y las matrices son la forma correcta de lidiar con eso.
Michael Homer
2

Además de la respuesta de @Michael Homer , puede usar la bash función eval :

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo "$PARMS"
eval "rsync ${PARMS} . "'"${TARGET}"'
Cuonglm
fuente
2
Puedes, pero no deberías. Se agregaron matrices específicamente para evitar este uso de eval.
chepner