Eu tinha n
vários arquivos em meu ../in/
diretório. Desses arquivos, quero que o arquivo mais antigo seja processado e enviado para o ../complete/
diretório. Os arquivos restantes são enviados para o ../error/
diretório. Alguém pode ajudar?
Responder1
Para copiar o arquivo mais antigo para ../complete
:
cp -v "$(find ../in -maxdepth 1 -type f -printf '%T@ %p\0' | sort -zn | \
sed -zn '1s/[0-9,\.]\+ //p')" ../complete
Para copiar todos, exceto o mais antigo, para ../error
:
find ../in -maxdepth 1 -type f -printf '%T@ %p\0' | sort -zn | \
sed -zn '2,$s/[0-9,\.]\+ //p' | xargs -0 cp -vt ../error
Explicação:
find
-maxdepth 1
desce apenas 1 nível; portanto, ele não pesquisará o diretório de forma recusada.-type f
corresponder apenas aos arquivos.-printf '...'
um formato de saída personalizado.%T@
fornece a hora da última modificação do arquivo em um carimbo de data/hora,%p
é o nome do arquivo. Observe o\0
no final. Os arquivos são impressos delimitados por bytes nulos, não delimitados por nova linha, porque a nova linha provavelmente poderia ser um caractere no nome do arquivo, o que quebraria o comando.
sort -zn
isso irá ler a saída delimitada por byte nulo (-z
) e classificá-la numericamente (-n
).sed -zn
lê a saída delimitada por byte nulo.1
corresponderá apenas à primeira linha (primeiro comando) e2,$
corresponderá a todas as outras (segundo comando)s/[0-9,\.]\+ //p
removerá o carimbo de data/hora inicial da saída.
- Somente a partir do segundo comando:
xargs -0 cp -vt ../error
processa a saída delimitada por byte nulo (-0
) e chamacp -vt ../error
todas as entradas, que copia os arquivos.
Exemplo de saída:
$ ls -lt ../in/
total 0
-rw-r--r-- 1 user user 0 Jun 24 08:18 file2
-rw-r--r-- 1 user user 0 Jun 24 08:18 file3
-rw-r--r-- 1 user user 0 Jun 24 08:18 file4
-rw-r--r-- 1 user user 0 Jun 24 08:18 file5
-rw-r--r-- 1 user user 0 Jun 24 08:17 file_oldest
$ cp -v "$(...)" ../complete
'../in/file_oldest' -> '../complete/file_oldest'
$
$ find ... | xargs ... ../error
'../in/file2' -> '../error/file2'
'../in/file3' -> '../error/file3'
'../in/file4' -> '../error/file4'
'../in/file5' -> '../error/file5'
Responder2
Para encontrar o arquivo mais antigo e movê-lo para o diretório desejado, use:
# cd to ../in/
$ ls -lt | grep -v '^d' | tail -1 | awk '{print $NF}' | xargs -I '{}' mv '{}' ../complete/
Agora que o arquivo mais antigo foi movido, você pode mover todos os arquivos para../error/
$ mv -- * ../error/
O --
seguinte mv
é necessário se você tiver algum nome de arquivo/diretório começando com -
.
Responder3
Construindo emA resposta de Raul:
$ ls -ltr | grep -v '^d' | awk 'NR==2 {print $NF; exit}' | xargs -I '{}' mv -- '{}' ../complete/
Notas:
- Você diz que quer fazer algo com “o arquivo mais antigo”. Todas as respostas pressupõem que você quer dizer modificado menos recentemente. Se você quer dizer menos recentementemudado, então diz; as respostas serão um pouco diferentes. Se você quer dizer menos recentementecriada, o que você quer é quase impossível no Unix.
ls -lt
(o que Rahul usou) lista o diretório atual em formato longo em ordem por data/hora de modificação, com o mais novo primeiro e o mais antigo por último.ls -ltr
é oRsempre disso; o mais antigo primeiro e o mais novo por último.- Se você tiver arquivos cujos nomes começam com um ponto (
.
), adicione aA
opção als
; por exemplo,ls -ltrA
. (A ordem das opções não importa; você pode usarls -ltAr
,ls -lAtr
, ou mesmols -ltr -A
se quiser.) - Eu menti. Uma listagem longa
ls
de um diretório sempre tem umatotal
linha primeiro. Emls -ltr
, a entrada mais antiga está nosegundolinha. grep -v '^d'
filtra os diretórios (se houver).awk 'NR==2 { print $NF; exit }'
imprime o último campo (o nome do arquivo*) da segunda linha (o arquivo mais antigo) e depois sai.- Este é um processo a menos que
tail -1 | awk '{print $NF}'
. - Isso pode funcionar mais rápido, porque, quando
awk
sai,ls
também pode sair, e você evitaria gerar a listagem para todo o diretório. Você pode eliminar isso
grep
fazendols -ltr | awk '! /^d/ { count++ } count==2 { print $NF; exit }'
mas eu não aconselharia isso. Scripts excessivamente complexos são difíceis de manter.
- Este é um processo a menos que
- O
--
depoismv
noxargs
comandoé necessário se você tiver algum nome de arquivo começando com um-
. Você realmente não precisa do
xargs
. Você pode simplificar o comando acima para$ mv -- $(ls -ltr | grep -v '^d' | awk 'NR==2 { print $NF; exit }') ../complete/
________
* Como a resposta de Rahul, isso falhará se o nome do arquivo contiver espaços ou tabulações - e pior ainda se contiver novas linhas.
Se você tiver arquivos cujos nomes começam com um ponto (.
), faça shopt -s dotglob
antes de fazer o
$ mv -- * ../error/