1.grep | xargs mv

1.grep | xargs mv

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:

  1. grep | xargs mv
  2. grep | parallel mv
  3. find -exec grep -q -exec mv

1.grep | xargs mv

Usar grepe xargs:

grep -FlZ "some string" *.txt \
| xargs -0 -I{} sh -c 'echo mv "$1" "${1%.*}.pdf" some/other/directory' xargs-sh {}

grep:

  • -Fcorresponde a uma string completa, não a um padrão
  • -lsaída apenas com nomes de arquivos correspondentes
  • -Zgerar 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:

  • -0lê dados delimitados por zero

2.grep | parallel mv

Com xargsvocê precisa chamar um subshell usando sh -cpara poder obter o nome do arquivo PDF correspondente.

Em vez de fazer isso, você também pode usar GNU parallelrecursos interessantes!!

grep -FlZ "some string" *.txt \
| parallel -0 -j1 echo mv {} {.}.pdf some/other/directory
  • -j1apenas 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 1para não recorrer
  • grep -qpara obter apenas o código de erro em vez dos nomes dos arquivos, o segundo -execsó será executado quando o primeiro for bem-sucedido.
  • A segunda -execé basicamente igual à xargsprimeira opção.
  • não precisamos fazer nenhuma saída delimitada por zero, porque não passamos nenhum nome de arquivo, findcuidamos disso.

Para cada opção, remova echose 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/doneconstrução, para a qual você passaria a lista de arquivos. Para cada arquivo você pode executaralgo, que terá acesso à $filevariá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 basenamecaso contrário o despojaria).

Responder3

Se você não tem parallelcomando, faça com dois xargscomo abaixo

grep -l 'search string' *.txt | xargs -I {} basename {} .txt | xargs -I {} cp {}.pdf /destination-directory

informação relacionada