1.grep | xargs mv

1.grep | xargs mv

Tengo algunos archivos como ese en un directorio.

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

y tengo que encontrar todos los archivos txt que contienen una cadena específica y luego mover este archivo txt y su archivo pdf correspondiente a otro directorio.

¿Cuál es la mejor manera de hacer esto?

Respuesta1

Tengo 3 opciones para ti, las ideas básicas son:

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

1.grep | xargs mv

Uso grepy xargs:

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

grep:

  • -Fcoincide con una cadena completa, no con un patrón
  • -lgenerar solo nombres de archivos coincidentes
  • -Zgenerar un delimitador cero entre nombres de archivos (esto es importante, porque a los nombres de archivos se les permite tener nuevas líneas, por lo que no puede confiar en una nueva línea como delimitador)

xargs:

  • -0lee datos delimitados por cero

2.grep | parallel mv

Debe xargsllamar a una subcapa usando sh -cpara poder obtener el nombre de archivo pdf correspondiente.

¡En lugar de hacer eso, también puedes utilizar GNU parallelfunciones interesantes!

grep -FlZ "some string" *.txt \
| parallel -0 -j1 echo mv {} {.}.pdf some/other/directory
  • -j1solo un trabajo a la vez
  • {}el nombre del archivo
  • {.}nombre de archivo sin extensión

3.find -exec grep -q -exec mv

Y otra 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 1no recurrir
  • grep -qpara obtener simplemente el código de error en lugar de los nombres de los archivos, el segundo -execsolo se ejecutará cuando el primero haya tenido éxito.
  • La segunda -execes básicamente la misma que xargsla primera opción.
  • No necesitamos hacer ninguna salida delimitada por ceros, porque no pasamos ningún nombre de archivo, findse encarga de ello.

Para cada opción, elimine echosi está satisfecho con el resultado.

Respuesta2

Divide el problema en pedazos.

"Necesito encontrar todos los archivos de texto que contengan una cadena determinada en el directorio actual".

grep -F "text string just as written (no regex)" *.txt

"Quiero elnombres de archivosde esos archivos"

grep -l -F "text string" *.txt

"Para cada uno de esos archivos..."

for...cada uno lo hace usando una while/do/doneconstrucción, a la que le pasaría la lista de archivos. Para cada archivo que puede ejecutaralgo, que tendrá acceso a la $filevariable:

grep -l -F "text string" *.txt \
| while IFS= read -r file ; do
...something...
done

El "algo" es

"...mover el archivo txt y el archivo pdf correspondiente..."

Entonces necesitas el nombre base, sin el ".txt".

BASE=$( basename ${file} .txt )

Y finalmente juntando todo:

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

(Tenga en cuenta que esto supone que cadaarchivoestá sin camino, porque basenamede lo contrario lo despojaría).

Respuesta3

Si no tiene parallelcomando, haga con dos xargscomo se muestra a continuación

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

información relacionada