sed: la coincidencia de patrones de un archivo devuelve múltiples resultados iguales en bash

CorePress2024-01-24  11

Estoy intentando extraer una lista de archivos definidos en mi archivo .gitattributes en bash.

El archivo .gitattributes tiene este aspecto

#
# Exclude these files from release archives.
# This will also make them unavailable when using Composer with `--prefer-dist`.
# https://blog.madewithlove.be/post/gitattributes/
#
/.git export-ignore
/.github export-ignore
/bin export-ignore
/wp-content/themes/**/.storybook export-ignore
/wp-content/themes/**/assets export-ignore
/wp-content/themes/**/storybook export-ignore
/wp-content/themes/**/tests export-ignore
/wp-content/themes/**/.editorconfig export-ignore
/wp-content/themes/**/.env.testing export-ignore
/wp-content/themes/**/.eslintignore export-ignore
/wp-content/themes/**/.eslintrc export-ignore
/wp-content/themes/**/.gitignore export-ignore
/wp-content/themes/**/.stylelintrc export-ignore
/wp-content/themes/**/babel.config.js export-ignore
/wp-content/themes/**/composer.json export-ignore
/wp-content/themes/**/composer.lock export-ignore
/wp-content/themes/**/package.json export-ignore
/wp-content/themes/**/package-lock.json export-ignore
/wp-content/themes/**/phpcs.xml.dist export-ignore
/wp-content/themes/**/phpstan.neon export-ignore
/wp-content/themes/**/phpstan.neon.dist export-ignore
/wp-content/themes/**/postcss.config.js export-ignore
/wp-content/themes/**/webpack.config.js export-ignore
/wp-content/themes/**/CODE_OF_CONDUCT.md export-ignore

composer.lock -diff
yarn.lock -diff
package.lock -diff

#
# Auto detect text files and perform LF normalization
# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
#
* text=auto

#
# The above will handle all files NOT found below
#
*.md text
*.php text
*.inc text

Mi script bash está dentro de la carpeta bin/ y mis atributos .gitattributes están en la raíz del proyecto.

sh bin/test.sh path

El script se ve así

#!/bin/bash

# - current_path variable (root)
file_list=()

while read -r line; do
  if [[ "$line" =~ (\/wp-content\/themes\/\*\*/) ]]; then
    newline=$(echo "$line" | sed 's/ export-ignore//p' | sed 's/\/wp-content\/themes\/\*\*\///p')
    file_list+=("$newline")
  fi
done <""/.gitattributes

echo "${file_list[@]}"

Pero esto me devolverá varios archivos duplicados (cuatro veces). Cuando ejecuto esto me sale

.storybook
.storybook
.storybook
.storybook assets
assets
assets
assets storybook
storybook
storybook
storybook tests
tests
tests
tests .editorconfig
.editorconfig
.editorconfig
.editorconfig .env.testing
.env.testing
.env.testing
.env.testing .eslintignore
.eslintignore
.eslintignore
.eslintignore .eslintrc
.eslintrc
.eslintrc
.eslintrc .gitignore
.gitignore
.gitignore
.gitignore .stylelintrc
.stylelintrc
.stylelintrc
.stylelintrc babel.config.js
babel.config.js
babel.config.js
babel.config.js composer.json
composer.json
composer.json
composer.json composer.lock
composer.lock
composer.lock
composer.lock package.json
package.json
package.json
package.json package-lock.json
package-lock.json
package-lock.json
package-lock.json phpcs.xml.dist
phpcs.xml.dist
phpcs.xml.dist
phpcs.xml.dist phpstan.neon
phpstan.neon
phpstan.neon
phpstan.neon phpstan.neon.dist
phpstan.neon.dist
phpstan.neon.dist
phpstan.neon.dist postcss.config.js
postcss.config.js
postcss.config.js
postcss.config.js webpack.config.js
webpack.config.js
webpack.config.js
webpack.config.js CODE_OF_CONDUCT.md
CODE_OF_CONDUCT.md
CODE_OF_CONDUCT.md
CODE_OF_CONDUCT.md

Resultado esperado:

.storybook
assets
storybook
tests
.editorconfig
.env.testing
.eslintignore
.eslintrc
.gitignore
.stylelintrc
babel.config.js
composer.json
composer.lock
package.json
package-lock.json
phpcs.xml.dist
phpstan.neon
phpstan.neon.dist
postcss.config.js
webpack.config.js
CODE_OF_CONDUCT.md

¿Qué estoy haciendo mal?

¿Cuál es el resultado esperado?

- anubhava

27/03/2021 a las 14:19

1

Igual que el anterior pero con un solo archivo, no varios. Gracias por el apoyo. Agregaré el resultado esperado 👍🏼

-dingo_d

27/03/2021 a las 14:35

1

Un consejo: no uses sh para ejecutar un script bash o cualquier otro script. Es como ejecutar sh ./my_python_script.py. En algunos sistemastem sh apunta a otra cosa y no a bash, así que espere algún resultado inesperado. Además, incrustar sed dentro de un bucle de lectura while no es ideal ya que sed tendrá que ejecutarse en cada línea, digamos que tiene 1k líneas para procesar y luego sed también tendrá que ejecutarse 1k veces. Sin mencionar que estás transmitiendo sed tras sed.

- Jetchisel

27/03/2021 a las 14:53

1

¡Gracias por el consejo! Arreglará esto. Este script debe ejecutarse en acciones de GH, así veré qué erroresLo sabré cuando lo probaré 👍🏼

-dingo_d

27/03/2021 a las 14:59



------------------------------------

Como probablemente otros señalarán, existen otras formas (más simples y más eficientes) de hacer lo que el OP busca hacer; El objetivo de esta respuesta es abordar el comportamiento del código sed actual del OP.

Por defecto, sed pasará la entrada a la salida estándar. Considere:

$ line='/wp-content/themes/**/.storybook export-ignore'
$ echo "${line}" | sed 's/ export-ignore//'
/wp-content/themes/**/.storybook

Al agregar la directiva p al comando sed, le estás diciendo a sed que imprima el resultado en la salida estándar. Considere:

$ line='/wp-content/themes/**/.storybook export-ignore'
$ echo "${line}" | sed 's/ export-ignore//p'
/wp-content/themes/**/.storybook
/wp-content/themes/**/.storybook

Como puede ver, obtenemos 2 conjuntos de resultados... un conjunto debido al comportamiento normal de sed... un conjunto debido a la directiva p adicional.

Si desea utilizar la directiva p y eliminar la salida 'duplicada', puede agregar el indicador -n (también conocido como --quiet/--silent) que deshabilita el comportamiento predeterminado de sed de pasar la entrada a la salida estándar. Considere:

$ line='/wp-content/themes/**/.storybook export-ignore'
$ echo "${line}" | sed -n 's/ export-ignore//p'
/wp-content/themes/**/.storybook

Debido a que tiene 2 comandos sed usando la directiva p, mientras no usa el indicador -n, termina con un total de 4 copias de cada entrada coincidente (el primer sed genera 2 líneas de salida; el segundo sed luego duplica la salida nuevamente).

Para eliminar los 'duplicados' hay un par de opciones:

elimine la directiva p de ambos comandos sed o ... agregue el indicador -n a ambos comandos sed

1

1

Excelente explicación y eso solucionó mi problema. No entendí por completo el objetivo de p, no uso bash con tanta frecuencia, así que pensé que es un delimitador para sed 😅

-dingo_d

27/03/2021 a las 14:47



------------------------------------

Esto se puede hacer usando un simple awk:

awk -F/ 'index(
while read -r line; do
  if [[ "$line" =~ (\/wp-content\/themes\/\*\*/) ]]; then
    echo "$line" | sed 's/ export-ignore//' | sed 's/\/wp-content\/themes\/\*\*\///'
  fi
done <""/.gitattributes
, "/wp-content/themes/") == 1 {sub(/ .*/, "", $NF); print $NF}' .gitattributes .storybook assets storybook tests .editorconfig .env.testing .eslintignore .eslintrc .gitignore .stylelintrc babel.config.js composer.json composer.lock package.json package-lock.json phpcs.xml.dist phpstan.neon phpstan.neon.dist postcss.config.js webpack.config.js CODE_OF_CONDUCT.md

awk Explicación:

-F/: Usar / como separador de campo de entrada index($0, "/wp-content/themes/") == 1: la línea comienza con /wp-content/themes/ solamente sub(/ .*/, "", $NF): Elimina cualquier cosa después del espacio en el último campo imprimir $NF: Imprimir lcampo ast

4

1

¡Guau, esa es una solución elegante! Aunque tendré que leer sobre awk para entender lo que hace XD

-dingo_d

27/03/2021 a las 14:49

1

Agregué una explicación en la respuesta

- anubhava

27/03/2021 a las 14:52

es posible que desee agregar /wp-content/temas/**/ a la mezcla para abordar el caso en el que .gitattributes contiene referencias a otros directorios (es decir, NF > 2 pero el usuario está interesado en un directorio específico, ¿tal vez enviado a awk a través de -v?)

-markp-fuso

27/03/2021 a las 15:04

1

Buen punto @markp-fuso, he modificado mi respuesta

- anubhava

27/03/2021 a las 15:08



------------------------------------

La solución rápida sería: simplemente canalizar la salida a través de sort -u :-)

La causa principal es el uso del modificador 'p' en la expresión regular sed. Esto imprime las copias adicionales. Puedes omitirlo gnu.org

Si necesita los resultados con un nombre de archivo por línea, yo crearía el script

< "/.gitattributes" awk '
/\/wp-content\/themes\/\*\*\// {
    gsub(/\/wp-content\/themes\/\*\*\//,"");
    gsub(/ export-ignore/,"");
    print 1008611;
}'

o, mejor aún, con awk

1008611

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare