scripts script resueltos programas paso pasar parametros manejo hacer ejercicios ejemplos como cadenas bash shell read-eval-print-loop

resueltos - scripts bash ejemplos



¿hay alguna manera de verificar si un script de bash está completo o no? (4)

Estoy tratando de implementar un REPL (read-eval-print loop) en bash. Si tal cosa ya existe, ignore lo siguiente y responda esta pregunta con un puntero.

test.sh esta secuencia de comandos como ejemplo ( test.sh nombre test.sh ):

if true then echo a else echo b fi echo c

Lo que quiero hacer es leer este script línea por línea, verificar si lo que he leído hasta ahora es una expresión completa de bash; Si está completo, eval ; De lo contrario, sigue leyendo la siguiente línea. La secuencia de comandos a continuación ilustra mi idea con suerte (aunque no funciona del todo).

x="" while read -r line do x=$x$''/n''$line # concatenate by /n # the line below is certainly a bad way to go if eval $x 2>/dev/null; then eval $x # code seems to be working, so eval it x="" # empty x, and start collecting code again else echo ''incomplete expression'' fi done < test.sh

Motivación

Para un script de bash, quiero analizarlo en expresiones sintácticamente completas, evaluar cada expresión, capturar la salida y finalmente marcar el código fuente y la salida (por ejemplo, usando Markdown / HTML / LaTeX / ...). Por ejemplo, para un script.

echo a echo b

Lo que quiero lograr es la salida como esta:

```bash echo a ``` ``` a ``` ```bash echo b ``` ``` b ```

En lugar de evaluar todo el script y capturar toda la salida:

```bash echo a echo b ``` ``` a b ```


En lugar de pidfiles, siempre que su script tenga un nombre identificable de forma exclusiva, puede hacer algo como esto:

#!/bin/bash COMMAND=$0 # exit if I am already running RUNNING=`ps --no-headers -C${COMMAND} | wc -l` if [ ${RUNNING} -gt 1 ]; then echo "Previous ${COMMAND} is still running." exit 1 fi ... rest of script ...


Los siguientes scripts deben generar el resultado de Markdown que espera.

eval "set -n; $x" se usa para verificar si el comando está completo, al verificar los errores de sintaxis en el comando. Solo un comando que no tenga errores de sintaxis se considerará completo, ejecutado y mostrado en el Markdown de salida.

Tenga en cuenta que el script de entrada que se procesará se ejecuta en un sub-shell y, por lo tanto, no interferirá con el script de procesamiento (es decir, el script de entrada puede usar los mismos nombres de variables que el script de procesamiento y no puede cambiar los valores de las variables). en el script de procesamiento). La única excepción son las variables especiales llamadas ___internal__variable___ .

Hay dos enfoques sobre cómo lograr eso, que presento a continuación. En la Versión 1 , cada vez que se procesa un nuevo comando completo, todas las declaraciones antes de que se ejecute para crear un "contexto" para el comando. Esto efectivamente ejecuta el script de entrada varias veces.

En la Versión 2 , el entorno del sub-shell se almacena en una variable después de que se ejecuta cada comando completo. Luego, antes de que se ejecute el siguiente comando, el entorno anterior se restaura en el sub-shell.

Versión 1

#!/bin/bash x="" # Current y="" # Context while IFS= read -r line # Keep indentation do [ -z "$line" ] && continue # Skip empty lines x=$x$''/n''$line # Build a complete command # Check current command for syntax errors if (eval "set -n; $x" 2> /dev/null) then # Run the input script up to the current command # Run context first and ignore the output ___internal_variable___="$x" out=$(eval "$y" &>/dev/null; eval "$___internal_variable___") # Generate command markdown echo "==================" echo echo "/`/`/`bash$x" echo "/`/`/`" echo # Generate output markdown if [ -n "$out" ] then echo "Output:" echo echo "/`/`/`" echo "$out" echo "/`/`/`" echo fi y=$y$''/n''$line # Build context x="" # Clear command fi done < input.sh

Versión 2

#!/bin/bash x="" # Current command y="true" # Saved environment while IFS= read -r line # Keep indentation do [ -z "$line" ] && continue # Skip empty lines x=$x$''/n''$line # Build a complete command # Check current command for syntax errors if (eval "set -n; $x" 2> /dev/null) then # Run the current command in the previously saved environment # Then store the output of the command as well as the new environment ___internal_variable_1___="$x" # The current command ___internal_variable_2___="$y" # Previously saved environment out=$(bash -c "${___internal_variable_2___}; printf ''<<<BEGIN>>>''; ${___internal_variable_1___}; printf ''<<<END>>>''; declare -p" 2>&1) # Separate the environment description from the command output y="${out#*<<<END>>>}" out="${out%%<<<END>>>*}" out="${out#*<<<BEGIN>>>}" # Generate command markdown echo "==================" echo echo "/`/`/`bash$x" echo "/`/`/`" echo # Generate output markdown if [ -n "$out" ] then echo "Output:" echo echo "/`/`/`" echo "$out" echo "/`/`/`" echo fi x="" # Clear command fi done < input.sh

Ejemplo

Para el script de entrada input.sh :

x=10 echo "$x" y=$(($x+1)) echo "$y" while [ "$y" -gt "0" ] do echo $y y=$(($y-1)) done

La salida será:

================== ```bash x=10 ``` ================== ```bash echo "$x" ``` Output: ``` 10 ``` ================== ```bash y=$(($x+1)) ``` ================== ```bash echo "$y" ``` Output: ``` 11 ``` ================== ```bash while [ "$y" -gt "0" ] do echo $y y=$(($y-1)) done ``` Output: ``` 11 10 9 8 7 6 5 4 3 2 1 ```


Supongamos que los comandos de prueba se almacenan en un archivo llamado "ejemplo". Es decir, usando los mismos comandos que en la respuesta anterior:

$ cat example x=3 echo "$x" y=$(($x+1)) echo "$y" while [ "$y" -gt "0" ] do echo $y y=$(($y-1)) done

El comando:

$ (echo ''PS1=; PROMPT_COMMAND="echo -n =====; echo"''; cat example2 ) | bash -i

produce:

===== x=3 ===== echo "$x" 3 ===== y=$(($x+1)) ===== echo "$y" 4 ===== ===== ===== while [ "$y" -gt "0" ] > do > echo $y > y=$(($y-1)) > done 4 3 2 1 ===== exit

Si también está interesado en los resultados intermedios de un bucle, el comando:

$ ( echo ''trap ''"''"''echo; echo command: $BASH_COMMAND; echo answer:''"''"'' DEBUG''; cat example ) | bash

resultados en:

command: x=3 answer: command: echo "$x" answer: 3 command: y=$(($x+1)) answer: command: echo "$y" answer: 4 command: [ "$y" -gt "0" ] answer: command: echo $y answer: 4 command: y=$(($y-1)) answer: command: [ "$y" -gt "0" ] answer: command: echo $y answer: 3 command: y=$(($y-1)) answer: command: [ "$y" -gt "0" ] answer: command: echo $y answer: 2 command: y=$(($y-1)) answer: command: [ "$y" -gt "0" ] answer: command: echo $y answer: 1 command: y=$(($y-1)) answer: command: [ "$y" -gt "0" ] answer:

Addendum 1

No es difícil cambiar los resultados anteriores a algún otro formato. Por ejemplo, este pequeño script de Perl:

$ cat formatter.pl #!/usr/bin/perl # $state=4; # 0: answer, 1: first line command, 2: more command, 4: unknown while(<>) { # print $state; if( /^===COMMAND===/ ) { print "===/n"; $state=1; next; } if( $state == 1 ) { print; $state=2; next; } if( $state == 2 && /^>+ (.*)/ ) { print "$1/n"; next; } if( $state == 2 ) { print "---/n"; $state=0; redo; } if( $state == 0 ) { print; next; } }

cuando se utiliza en el comando:

( echo ''PS1="===COMMAND===/n"''; cat example ) | bash -i 2>&1 | ./formatter.pl

da este resultado:

=== x=3 === echo "$x" --- 3 === y=$(($x+1)) === echo "$y" --- 4 === === === while [ "$y" -gt "0" ] do echo $y y=$(($y-1)) done --- 4 3 2 1 === exit


bash -n -c "$command_text"

... determinará si su $command_text es un script sintácticamente válido sin ejecutarlo realmente.

Tenga en cuenta que hay una gran cantidad de espacio entre "sintácticamente válido" y "correcto". Considere adoptar algo como http://shellcheck.net/ si desea analizar correctamente el idioma.