Copie varios archivos a través de cp desde una cadena

Copie varios archivos a través de cp desde una cadena

Estoy intentando copiar varios archivos a un directorio desde un shellscript. Estos archivos contienen todo tipo de caracteres "feos", como espacios en blanco, corchetes y mucho más. Sin embargo, estoy estancado cuando se trata de escapar de estos bashy cpparece que los manejo de manera muy extraña.

Este es el escenario:
cuando emito este comando desde mi shell, funciona de maravilla:
cp /somedir/a\ file.png /somedir/another\ file.png /someotherdir/

Sin embargo, al leer los archivos para copiar desde una cadena, de repente se vuelve extraño: los resultados son
var="/somedir/a\ file.png /somedir/another\ file.png"
cp "$var" /someotherdir/

cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory

Creo que esto se debe al hecho de que doy la variable como una cadena y cpcreo que es un archivo, aunque son varios archivos. Al emitir el mismo comando sin poner la variable entre comillas ( var="/somedir/a\ file.png /somedir/another\ file.png"; cp $var /someotherdir/), aparece un error aún más extraño:
cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory
cp: cannot stat 'file.png': No such file or directory
cp: cannot stat 'another\\': No such file or directory
cp: cannot stat 'file.png': No such file or directory

Parece ignorar por completo mi escape. ¿Qué estoy haciendo mal?

EDITAR:// Parece queCopiar lista de archivostiene una respuesta con xargs, pero todavía me pregunto por qué bash actúa tan extraño aquí.

Respuesta1

Doctor, duele cuando hago esto...

¡Entonces no hagas eso!

En serio, ¿por qué intentas asignar a una sola variable una lista de nombres de archivos separados por espacios que contienen espacios? Ésa es una receta para el desastre.

Para responder a su pregunta implícita: estoy de acuerdo en que los mensajes de error que muestra no tienen sentido para los comandos que dice haber utilizado.

Como no ha dicho cómo determina qué archivos copiar, es difícil saber qué solución funcionará para usted y cuál no. He aquí un posible enfoque:


f1="/somedir/a archivo.png"
f2="/somedir/otro archivo.png"
cp "$f1" "$f2" "$f3" … /algúnotrodir

(No necesita el /final de /someotherdir/, pero tampoco hace daño). Esto está limitado por el hecho de que necesita saber de antemano la cantidad máxima de archivos que necesitará copiar, y necesita algunos forma de saber a qué fvariable asignar cada uno.

Esto se acerca a lo que estás haciendo ahora:

var="/somedir/a\ archivo.png /somedir/otro\ archivo.png …"
eco "$var" | mientras lee f1 f2 f3…
hacer
    cp "$f1" "$f2" "$f3" … /algúnotrodir
hecho

El readcomando dividirá la $varcadena en los espacios simples y eliminará las barras invertidas, dejando solo  (espacio). Pero aún necesita saber de antemano la cantidad máxima de archivos que necesitará copiar. (Necesita la whileconstrucción del bucle aunque este bucle se repetirá solo una vez).

Quizás este se esté acercando:

lista de archivos=/tmp/mi_lista de archivos.$$
echo "/somedir/a archivo.png" >> "$listadearchivos"
echo "/somedir/otro archivo.png" >> "$listadearchivos"
mientras lee el nombre del archivo
hacer
    cp "$nombre de archivo" /algún otrodir
hecho < "$lista de archivos"

Otra variación es utilizar una matriz de variables. Puede aprender cómo hacerlo en la bashpágina de manual.

Respuesta2

Cuando bash analiza una línea de comando, primero interpreta comillas, escapes y demás, y luego reemplaza las variables. Si hay escapes, comillas, etc. en los valores de las variables, nunca tendrán efecto porque cuando se incluyen en el comando, ya es demasiado tarde para que tengan el efecto deseado.

Generalmente, la mejor manera de manejar este tipo de problema es almacenar los nombres de los archivos en una matriz (un elemento por nombre de archivo) en lugar de una variable de cadena; luego use "${array[@]}"para expandir la matriz y cada elemento se trate como una "palabra" separada:

files=(/somedir/a\ file.png /somedir/another\ file.png)
cp "${files[@]}" /someotherdir/

Actualización: si necesita crear la lista de archivos dinámicamente, es fácil con una matriz:

files=() # Start with an empty list
for newfile in somethingorother; do
    files+=("$newfile")
done

Respuesta3

Si estás usando bash, puedes usar comillas dobles en el nombre del archivo para evitar el uso de caracteres de escape:

cp /old_location/This\ is\ an\ \[ugly\] \file.tmp /new_location/This\ is\ an\ \[ugly\] \file.tmp

use esto en su lugar cuando lo use #!/bin/bashen su shellscript:

cp "/old_location/This is an [ugly] file with extras $$$.tmp" "/new_location/Ugly file with extras $$$.tmp"

En su caso, está utilizando los valores dentro de una variable, pero está pasando la variable incorrectamente. Si una variable tiene espacios, también debes comillarla dos veces:

var="/somedir/a\ file.png /somedir/another\ file.png"; cp "$var" /someotherdir/

Si no lo pones entre comillas será como usar varias variables separadas por espacios.

información relacionada