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:
grep | xargs mv
grep | parallel mv
find -exec grep -q -exec mv
1.grep | xargs mv
Uso grep
y xargs
:
grep -FlZ "some string" *.txt \
| xargs -0 -I{} sh -c 'echo mv "$1" "${1%.*}.pdf" some/other/directory' xargs-sh {}
grep
:
-F
coincide con una cadena completa, no con un patrón-l
generar solo nombres de archivos coincidentes-Z
generar 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
:
-0
lee datos delimitados por cero
2.grep | parallel mv
Debe xargs
llamar a una subcapa usando sh -c
para poder obtener el nombre de archivo pdf correspondiente.
¡En lugar de hacer eso, también puedes utilizar GNU parallel
funciones interesantes!
grep -FlZ "some string" *.txt \
| parallel -0 -j1 echo mv {} {.}.pdf some/other/directory
-j1
solo 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 1
no recurrirgrep -q
para obtener simplemente el código de error en lugar de los nombres de los archivos, el segundo-exec
solo se ejecutará cuando el primero haya tenido éxito.- La segunda
-exec
es básicamente la misma quexargs
la primera opción. - No necesitamos hacer ninguna salida delimitada por ceros, porque no pasamos ningún nombre de archivo,
find
se encarga de ello.
Para cada opción, elimine echo
si 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/done
construcción, a la que le pasaría la lista de archivos. Para cada archivo que puede ejecutaralgo, que tendrá acceso a la $file
variable:
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 basename
de lo contrario lo despojaría).
Respuesta3
Si no tiene parallel
comando, haga con dos xargs
como se muestra a continuación
grep -l 'search string' *.txt | xargs -I {} basename {} .txt | xargs -I {} cp {}.pdf /destination-directory