Estoy intentando utilizar el siguiente código para omitir el último archivo mientras copio el resto en una carpeta de destino:
ls -Art | tail -n 1 | xargs -I% cp !(%) ~/test_dst/folder1
Las dos primeras canalizaciones obtienen el nombre del archivo más reciente, y esto se canaliza a xargs, lo que lo agrega al comando cp, donde uso extglob. comodín para copiar todo lo demás. Sin embargo, el problema es que esto copia todos los archivos. Intenté depurar esto determinando qué imprime la primera parte de esto (set -x está activado):
> ~/test_src/folder1$ ls -Art | tail -n 1
+ tail -n 1
+ ls --color=auto -Art
file4
¡éxito! ¡Este es el archivo correcto! Luego intenté imprimirlo con el comodín:
> ~/test_src/folder1$ ls -Art | tail -n 1 | xargs -I% echo !(%)
+ tail -n 1
+ ls --color=auto -Art
+ xargs -I% echo file1 file2 file3 file4
file1 file2 file3 file4
¿E incluye el archivo omitido? Intenté codificar el comando por sí solo y, ¿sabes?, en realidad funciona bien:
> ~/test_src/folder1$ echo !(file4)
+ echo file1 file2 file3
file1 file2 file3
¿Qué estoy haciendo mal? Este es básicamente mi primer intento con un script de shell y me está volviendo loco. Cualquier consejo es muy apreciado. Preferiría depurar este método específico (esta es la forma en que pensé hacerlo), pero definitivamente estoy abierto a cualquier sugerencia o método de reemplazo para hacerlo. ¡gracias!
Respuesta1
Sí, no va a funcionar. Las dos funciones que intenta combinar se interpretan en el orden incorrecto.
Los extglobs son una característica del shell (el intérprete de comandos) y, como habrás notado en el resultado de "trace", bash los expande.antesel comando 'xargs' realmente se ejecuta.
Si los extglobs (o cualquier glob, en realidad) se pasaran tal cual a xargs (o a 'echo', o a 'cp'), no sabría qué hacer con ellos; por ejemplo, 'cp' pensaría que "!(%)" o "!(archivo1)" es literalmente el nombre del archivo que se va a copiar.
xargs, por otro lado, esnoparte del shell: es un programa independiente como cualquier otro, y bash no conoce el significado del "%", ni sabe que estás pasando otro comando a xargs.
Entonces, cuando !(%)
se expande el extglob, solo se expandeuna vezpara todo el comando, no una vez por cada entrada xargs, sino solo una vez para el literal %
que Bash ve en el comando. Esto por supuesto resulta entodos los archivos que no tienen nombre%
.
Una forma de hacer esto es reemplazar xargs con las propias expansiones del shell; a diferencia de xargs, las variables del shell se expandenantesextglobs, entonces si tuvieras el nombre del archivo en una variable...
except_file=$(ls -Art | tail -n 1)
... podrías usar eso en el extglob:
cp !("$except_file") ~/test_dst/folder1
(Los dos se pueden combinar en cp !("$(ls...)") ~/...
).
También es posible hacer lo contrario y mantener xargs, pero eliminar extglob. En lugar de obtener solo el último archivo tail
y luego necesitar pasos adicionales para obtener todo lo demás, puede obtener directamentetodo exceptoel último archivo usado head
(los parámetros negativos tanto para 'cabeza' como para 'cola' significan "excepto la última N"):
ls -Art | head -n -1 | xargs -I% cp % ~/test_dst/folder1
o, para permitir que xargs haga su trabajo de invocar uno cp
para todos los archivos a la vez, suponiendo que su sistema tenga GNU Coreutils con la cp -t <target>
opción:
ls -Art | head -n -1 | xargs -d '\n' cp -t ~/test_dst/folder1
Nota al margen: los nombres de archivos en Linux pueden contener saltos de línea. Muchos de los ejemplos anteriores suponen que usted no tiene esos nombres de archivos, porque realmente no debería hacerlo, peroesalgo a considerar al escribir scripts para implementar en sistemas desconocidos, y la programación "defensiva" normalmente involucraría herramientas como find ... -print0
y xargs -0
para trabajar con listas separadas por nulos, en lugar de listas separadas por nuevas líneas. (Los comodines de Shell generalmente lo solucionan bien).