Tengo muchos archivos que están ordenados por nombre de archivo en un directorio. Deseo copiar los archivos finales N (por ejemplo, N = 4) a mi directorio de inicio. ¿Cómo debería hacerlo?
cp ./<the final 4 files> ~/
Tengo muchos archivos que están ordenados por nombre de archivo en un directorio. Deseo copiar los archivos finales N (por ejemplo, N = 4) a mi directorio de inicio. ¿Cómo debería hacerlo?
cp ./<the final 4 files> ~/
Respuestas:
Esto se puede hacer fácilmente con las matrices bash / ksh93 / zsh:
a=(*)
cp -- "${a[@]: -4}" ~/
Esto funciona para todos los nombres de archivos no ocultos, incluso si contienen espacios, pestañas, líneas nuevas u otros caracteres difíciles (suponiendo que haya al menos 4 archivos no ocultos en el directorio actual con bash
).
a=(*)
Esto crea una matriz a
con todos los nombres de archivo. Los nombres de archivo devueltos por bash están ordenados alfabéticamente. (Supongo que esto es lo que quiere decir con "ordenado por nombre de archivo" ).
${a[@]: -4}
Esto devuelve los últimos cuatro elementos de la matriz a
(siempre que la matriz contenga al menos 4 elementos con bash
).
cp -- "${a[@]: -4}" ~/
Esto copia los últimos cuatro nombres de archivo a su directorio de inicio.
Esto copiará los últimos cuatro archivos solo al directorio de inicio y, al mismo tiempo, antepondrá la cadena a_
al nombre del archivo copiado:
a=(*)
for fname in "${a[@]: -4}"; do cp -- "$fname" ~/a_"$fname"; done
Si usamos en a=(./some_dir/*)
lugar de a=(*)
, entonces tenemos el problema de que el directorio se adjunta al nombre del archivo. Una solución es:
a=(./some_dir/*)
for f in "${a[@]: -4}"; do cp "$f" ~/a_"${f##*/}"; done
Otra solución es usar una subshell y cd
el directorio en la subshell:
(cd ./some_dir && a=(*) && for f in "${a[@]: -4}"; do cp -- "$f" ~/a_"$f"; done)
Cuando se completa el subshell, el shell nos devuelve al directorio original.
La pregunta pide archivos "ordenados por nombre de archivo". Ese orden, señala Olivier Dulac en los comentarios, variará de un lugar a otro. Si es importante tener resultados fijos independientes de la configuración de la máquina, lo mejor es especificar la configuración regional explícitamente cuando a
se define la matriz . Por ejemplo:
LC_ALL=C a=(*)
Puede averiguar en qué localidad se encuentra actualmente ejecutando el locale
comando.
Si está utilizando zsh
, puede incluir entre paréntesis ()
una lista de los llamados calificadores globales que seleccionan los archivos deseados. En su caso, eso sería
cp *(On[1,4]) ~/
Aquí On
ordena los nombres de los archivos alfabéticamente en orden inverso y [1,4]
solo toma los primeros 4 de ellos.
Puede hacer esto más robusto seleccionando solo archivos simples (excluyendo directorios, tuberías, etc.) con .
, y también agregando --
un cp
comando para tratar los archivos cuyos nombres comienzan -
correctamente, por lo que:
cp -- *(.On[1,4]) ~
Agregue el D
calificador si también desea considerar archivos ocultos (archivos de puntos):
cp -- *(D.On[1,4]) ~
*([-4,-1])
.
on
) es el comportamiento predeterminado, por lo que no es necesario especificar esto, y -
frente a los números se cuentan los nombres de los archivos desde la parte inferior.
Aquí hay una solución que usa comandos bash extremadamente simples.
find . -maxdepth 1 -type f | sort | tail -n 4 | while read -r file; do cp "$file" ~/; done
Explicación:
find . -maxdepth 1 -type f
Encuentra todos los archivos en el directorio actual.
sort
Se ordena alfabéticamente.
tail -n 4
Mostrar solo las últimas 4 líneas.
while read -r file; do cp "$file" ~/; done
Recorre cada línea que ejecuta el comando copiar.
Siempre que encuentre que el tipo de shell es agradable, puede hacer lo siguiente:
set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
cp "$@" /path/to/target/dir
Esto es muy similar a la bash
solución de matriz específica ofrecida, pero debería ser portátil a cualquier shell compatible con POSIX. Algunas notas sobre ambos métodos:
Es importante que usted dirige sus cp
argumentos w / cp --
o se obtiene uno de entre .
un punto o /
en la cabecera de cada nombre. Si no lo hace, se arriesga a un -
guión principal en cp
el primer argumento que puede interpretarse como una opción y provocar que la operación falle o que, de lo contrario, arroje resultados no deseados.
set -- ./*
o array=(./*)
.Es importante cuando se usan ambos métodos para garantizar que tenga al menos tantos elementos en su matriz arg como intente eliminar; lo hago aquí con una expansión matemática. Lo shift
único ocurre si hay al menos 4 elementos en la matriz arg, y luego solo aleja esos primeros argumentos que generan un excedente de 4.
set 1 2 3 4 5
caso, cambiará la 1
distancia, pero en un set 1 2 3
caso, no cambiará nada.a=(1 2 3); echo "${a[@]: -4}"
imprime una línea en blanco.Si está copiando de un directorio a otro, puede usar pax
:
set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
pax -rws '|.*/|new_prefix|' "$@" /path/to/target/dir
... que aplicaría una sed
sustitución de estilo a todos los nombres de archivos a medida que los copia.
Si solo hay archivos y sus nombres no contienen espacios en blanco o nueva línea (y no ha modificado $IFS
) o caracteres globales (o algunos caracteres no imprimibles con algunas implementaciones de ls
), y no comience con .
, entonces puede hacer esto :
cp -- $(ls | tail -n 4) ~/
a_
a los cuatro archivos? Lo intenté echo "a_$(ls | tail -n 4)"
, pero solo antepone el primer archivo.
ls
salida de análisis se considera una forma incorrecta porque generalmente falla horriblemente en los nombres de archivo que contienen no solo espacios, sino también pestañas, líneas nuevas u otros caracteres válidos pero "difíciles".
Esta es una solución bash simple sin ningún código de bucle. Copie los últimos 4 archivos del directorio actual al destino:
$ find . -maxdepth 1 -type f |tail -n -4|xargs cp -t "$destdir"
find
le da los archivos en el orden deseado? ¿Cómo se asegura de que los archivos que contienen caracteres de espacio en blanco (incluidas las nuevas líneas) en sus nombres se manejan correctamente?
LC_ALL=C