bash: ¿Existe alguna manera de atrapar la salida de los comandos de "selección" de Linux que no cumplen con sus criterios de coi

CorePress2024-01-25  8

Por "selección" comandos, me refiero a comandos que filtran como grep, find, etc.

Fondo

Existen al menos algunos entornos de mainframe IBM diferentes que admiten el procesamiento de canalizaciones (CMS Pipelines, por ejemplo). No es una construcción de shell como lo es en Bash, sino generalmente un comando PIPE dedicado que tiene sus propios subcomandos integrados (etapas) que realizan el filtrado y el procesamiento de datos.

En mi opinión, una de las características más interesantes es que las "etapas de selección" que realizan algún tipo de filtrado normalmente admiten múltiples flujos de salida. Aquellas líneas de datos que cumplen con los criterios de selección se pasan al flujo de salida primario y, si se especifica, las que no lo hacen se pasan a un flujo de salida secundario, donde pueden pasar por un proceso completo.Secuencia de procesamiento muy diferente.

Tomando el ejemplo de la página de Wikipedia vinculada anteriormente, que podría aparecer en un programa REXX:

'PIPE (END ?) < INPUT TXT',  /* read contents of file INPUT TXT                   */
'|A: LOCATE /Hello/',        /* find all lines containing "Hello"                 */
'|INSERT / World!/ AFTER',   /* give those to INSERT to append " World!"          */
'|B: FANINANY',              /* pass to FANINANY, accepts multiple input streams  */
'|> NEWFILE TXT A',          /* write all contents to file NEWFILE TXT A          */
'?A:',                       /* end this pipeline, 2nd output of LOCATE goes here */
'|XLATE UPPER',              /* translate text to uppercase                       */
'|B:'                        /* feed back into FANINANY stage above               */

La segunda aparición de la etiqueta A: conecta el segundo flujo de salida de LOCATE (en este caso, líneas del archivo de entrada que no coinciden con "Hola") al flujo de entrada de XLATE, que convierte los datos a mayúsculas. y lo pasa de regreso a la primera etiqueta B: (FANINANY). FANINANY acepta más de un flujo de entrada y leerá todos los flujos de entrada conectados simultáneamente, preservando el orden de los datos.

¿El signo de interrogación? sirve como carácter final en este ejemplo y le dice al procesador de comandos "este es el final de la primera canalización". de modo que lo que sigue se pueda utilizar para conectar de forma independiente otro pipeline a otras etapas etiquetadas, lo que le permite especificar la tubería completa en un solo comando.

Ejemplo de archivo INPUT TXT:

foo
Hello
bar
Hello
baz
Hello

Después de este PIPE, el archivo NEWFILE TXT A contendría:

FOO
Hello World!
BAR
Hello World!
BAZ
Hello World!
Pregunta

Mi pregunta principal es: ¿es posible lograr algo como esto en Bash?

Creo que el marco está implementado con canalizaciones con nombre (mkfifo, etc.) y sustitución de procesos (con los cuales estoy familiarizado).

Pero la pieza crítica del rompecabezas es: supongo que si un comando de Linux/UNIX hará eco de toda su salida en diferentes lugares depende del comando individual y de si fue escrito para hacer eso. De lo contrario, sospecho que habría que modificar el código, después de lo cual posiblemente podría usar construcciones Bash para lograr este tipo de cosas.

1

Puedes hacer algo como esto con awk, imprimiendo en diferentes archivos.

-Barmar

28 de marzo de 2021 a las 2:04



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

Los programas Unix (/Linux) generalmente no admiten múltiples flujos de salida, aunque con algunos como awk y perl ciertamente se pueden escribir scripts que envíen resultados a múltiples lugares. Aquí hay un script awk simple que envía líneas coincidentes a la salida estándar,y los que no coinciden con una canalización con nombre:

awk -v nomatch="/path/to/pipe" '{if (
awk '{if (
while IFS= read -r line; do
    if [[ "$line" =~ Hello ]]; then
        printf '%s World!\n' "$line"
    else
        tr "[:lower:]" "[:upper:]" <<<"$line"
    fi
done <input.txt >newfile.txt
~ /Hello/) {print
$ cd "$(mktemp --directory)"
$ cat > input.txt <<'EOF'
foo
Hello
bar
Hello
baz
Hello
EOF
$ sed '/Hello/! s/\(.*\)/\U/;s/\(Hello\)/ World!/;' input.txt
FOO
Hello World!
BAR
Hello World!
BAZ
Hello World!
" World!"} else {print toupper(
while IFS= read -r line
do
    if [[ "$line" == 'Hello' ]]
    then
        echo "${line} World!"
    else
        echo "${line^^}"
    fi
done < input.txt
)}}' input.txt >newfile.txt
~ /Hello/) {print} else {print 1008611 > nomatch}}'

Pero parece que quieres recombinar los flujos de una manera coherente (es decir, manteniendo el orden de las líneas), y no hay una buena manera de hacerlo, ya que los datos fluyen de forma independiente a través de cada flujo (tal vez a diferentes velocidades, con canales independientes). almacenamiento en búfer, etc.).

Pero parece que lo que realmente quieres es un único programa que transforme diferentes líneas de manera diferente, y hay varios programas Unix que harán eso. En orden aproximadamente creciente de potencia y flexibilidad (y complejidad), los estándar son sed, awk y perl, pero puedes usar prácticamente cualquier cosa (¡incluso bash!). Aquí hay un ejemplo en awk:

1008611

Y aquí está el equivalente en bash:

1008611

(con versiones más nuevas de bash, puedes reemplazar tcomando hat tr con solo printf '%s\n' "${line^^}")

3

Sí, eché un vistazo a awk brevemente y parece que hará este tipo de cosas con bastante facilidad. Estaba pensando que se podría usar una combinación de awk y canalizaciones con nombre para hacer el truco para algo como esto, en términos generales (por supuesto, para ese ejemplo de entrada simple, eso sería excesivo). Pero entonces, como dijiste, ¿sería capaz de sincronizar todas las entradas de manera que se conserve el orden original? Supongo que la respuesta es "tal vez". EsoProbablemente este sea uno de los beneficios de tener un comando PIPE dedicado que pueda supervisar todo eso.

-David Mordigal

28 de marzo de 2021 a las 2:31

En realidad, al observar estos "canalizaciones" un poco más, realmente los consideraría una forma primitiva del mismo concepto que sed, awk, perl, etc. No se procesan a través de programas externos (como tuberías de Unix) sino a través de programas integrados. funciones (tal como lo hacen sed, etc.), y no veo ninguna diferencia real entre sus "etapas de selección" y pasos de procesamiento condicional en sed, etc. (en todo caso, sony son más feos, porque se basan en una sintaxis de ir y etiquetar, en lugar de una estructura estructurada si-entonces-si no, como lo hacen awk y perl). Evitaría intentar replicarlos, solo usaría herramientas modernas.

- Gordon Davisson

28 de marzo de 2021 a las 2:53

Bueno, CMS PIPE no es un lenguaje per se, es solo un comando. Es muy parecido a sed en que el comando pipe que escribes es una especie de especificación de procesamiento que actúa sobre los datos. Y tienes razón en que se centra en funciones integradas.lidad, por lo que se podría argumentar que eso la hace más primitiva porque no existe un "lenguaje" y la funcionalidad es limitada. Pero la sintaxis de la etiqueta es sólo la versión de IBM del sistema de "redirección". para los componentes individuales que admiten múltiples flujos de entrada/salida. Esa parte es lo que creo que lo hace realmente poderoso, al menos para ciertas aplicaciones.

-David Mordigal

28 de marzo de 2021 a las 3:26



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

Puedes realizar fácilmente ambas transformaciones con un solo comando con el operador de coincidencia negativa de GNU sed:

1008611

No creo que exista una manera eficiente de enviar parte de la entrada estándar a un comando y part de él a otro comando (pregunta relevante 1, 2), ya que cualquier comando que filtre la entrada estándar necesariamente también lo consumirá todo. Algo como

1008611

Desafortunadamente es muy lento.

Como alternativa, puedes enviar el mismo archivo a dos flujos de entrada diferentes usando COMMAND < entrada.txt 3< entrada.txt.

1

Sí, pensé que es bastante fácil hacer cosas simples como esta en sed o incluso en Bash. También estaba pensando de manera más general, ¿hay alguna manera de redirigir la salida parcial a un lugar diferente?s, pero a menos que el comando en cuestión haya sido escrito para eso, suena como no. Barmar y Gordon también mencionaron awk, que probablemente sería mejor para escenarios que no implican volver a fusionar todos los resultados.

-David Mordigal

28 de marzo de 2021 a las 2:19

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