Eu tenho alguns arquivos assim em um diretório
00.01.02 0000 some filename 1.pdf
00.01.02 0000 some filename 1.txt
02.03.07 0163 another filename 2.pdf
02.03.07 0163 another filename 2.txt
e eu tenho que encontrar todos os arquivos txt contendo uma string específica e então mover esse arquivo txt e seu arquivo pdf correspondente para outro diretório.
Qual é a melhor maneira de fazer isso?
Responder1
Eu tenho 3 opções para você, as ideias básicas são:
grep | xargs mv
grep | parallel mv
find -exec grep -q -exec mv
1.grep | xargs mv
Usar grep
e xargs
:
grep -FlZ "some string" *.txt \
| xargs -0 -I{} sh -c 'echo mv "$1" "${1%.*}.pdf" some/other/directory' xargs-sh {}
grep
:
-F
corresponde a uma string completa, não a um padrão-l
saída apenas com nomes de arquivos correspondentes-Z
gerar delimitador zero entre nomes de arquivos (isso é importante, porque os nomes de arquivos podem ter novas linhas, portanto você não pode confiar em uma nova linha como delimitador)
xargs
:
-0
lê dados delimitados por zero
2.grep | parallel mv
Com xargs
você precisa chamar um subshell usando sh -c
para poder obter o nome do arquivo PDF correspondente.
Em vez de fazer isso, você também pode usar GNU parallel
recursos interessantes!!
grep -FlZ "some string" *.txt \
| parallel -0 -j1 echo mv {} {.}.pdf some/other/directory
-j1
apenas um trabalho por vez{}
o nome do arquivo{.}
nome de arquivo sem extensão
3.find -exec grep -q -exec mv
E outra alternativa, usando find
:
find . -maxdepth 1 -name "*.txt" \
-exec grep -Fq "some string" {} \; \
-exec sh -c 'echo mv "$1" "${1%.*}.pdf" some/other/location' find-sh {} \;
-maxdepth 1
para não recorrergrep -q
para obter apenas o código de erro em vez dos nomes dos arquivos, o segundo-exec
só será executado quando o primeiro for bem-sucedido.- A segunda
-exec
é basicamente igual àxargs
primeira opção. - não precisamos fazer nenhuma saída delimitada por zero, porque não passamos nenhum nome de arquivo,
find
cuidamos disso.
Para cada opção, remova echo
se estiver satisfeito com o resultado.
Responder2
Divida o problema em pedaços.
"Preciso encontrar todos os arquivos de texto que contenham uma determinada string no diretório atual".
grep -F "text string just as written (no regex)" *.txt
"Eu quero onomes de arquivosdesses arquivos"
grep -l -F "text string" *.txt
"Para cada um desses arquivos..."
for...cada um que você faz usando uma while/do/done
construção, para a qual você passaria a lista de arquivos. Para cada arquivo você pode executaralgo, que terá acesso à $file
variável:
grep -l -F "text string" *.txt \
| while IFS= read -r file ; do
...something...
done
O “algo” é
"...mova o arquivo txt e o arquivo pdf correspondente..."
Então você precisa do nome base, sem o ".txt"
BASE=$( basename ${file} .txt )
E finalmente juntando tudo:
grep -l -F "text string" *.txt \
| while IFS= read -r file ; do \
BASE=$( basename ${file} .txt )
mv ${BASE}.txt /some/other/dir
mv ${BASE}.pdf /some/other/dir
done
(Observe que isso pressupõe que cadaarquivoestá sem caminho, porque basename
caso contrário o despojaria).
Responder3
Se você não tem parallel
comando, faça com dois xargs
como abaixo
grep -l 'search string' *.txt | xargs -I {} basename {} .txt | xargs -I {} cp {}.pdf /destination-directory