Cómo afirmar que una cadena tiene un carácter de nueva línea y, si es así, eliminarlo

9

Tengo una cadena que es el resultado de alguna operación sobre la que no tengo control. Cuando imprimo esta variable usando echo, obtengo:

echo $myvar
hello

Sin embargo, cuando lo hago

if [ $myvar = "hello" ]; then
    echo they are equal
else
    echo they are not equal
fi

Siempre entiendo que no son iguales. Sospecho que esto se debe a un newlinepersonaje.

La cadena también se comporta de manera extraña. Cuando lo hago:

newVAR="this is my var twice: "$myvar$myvar
echo $newVAR

Yo obtengo:

hellois my var twice: hello

¿Cómo puedo verificar si esto se debe realmente a una newliney, de ser así, eliminarlo?

farid99
fuente
1
En Bash puedes printf '%q\n' "$string"obtener una versión escapada de cualquier cadena. Por ejemplo: printf '%q\n' 'foo\n'-> foo\\n; printf '%q\n' $'foo\n'->$'foo\n'
l0b0
1
No estás citando la expansión de ninguna de tus variables. Si tuvieran algún espacio en blanco al final, no lo verías con echo $foo. Hazlo en su echo "$foo"lugar.
Peter Cordes

Respuestas:

9

El problema es que tiene un retorno de carro incorporado (CR, por sus \rsiglas en inglés). Esto hace que el punto de inserción de texto del terminal regrese al inicio de la línea que está imprimiendo. Es por eso que está viendo el 'hola' al comienzo de la línea en su $newVARejemplo: sed -n lmuestra una vista legible de caracteres no imprimibles (y al final de la línea).

var=ab$'\r'c ; echo "$var";  printf %s "$var" | sed -n l
# output:
cb
ab\rc$

Puede probarlo con una simple verificación de condición de bash:

[[ $var == *$'\r'* ]] && echo yes || echo no
# output:
yes

Puede combinar la prueba y la reparación en un solo paso probando \r(s) y eliminándolas mediante:

fix="${var//$'\r'/}"; echo "$var"; echo "$fix"
# output:
cb
abc

La solución utiliza la expansión de parámetros de Shell . La forma particular utilizada anteriormente es para reemplazar las subcadenas basadas en su patrón proporcionado: ${parameter/pattern/string}<- Esto reemplaza solo el primer patrón encontrado con una cadena en la variable llamada parámetro *. Para reemplazar todos los patrones, solo necesita cambiar el primero /a //.

Peter.O
fuente
¿podrías explicar tu último fragmento de código? la fix="....linea?
farid99
@ farid99: explicación añadida a la respuesta, la nota fixpuede ser varen sí - o, a menudo sólo puede utilizar la expansión de parámetros tal cual, sin la necesidad de reasignar el (posiblemente) el valor modificado ..
Peter.O
5

Puedes representar \rcomo $'\r'en bash:

if [ "$myvar" = "hello"$'\r' ]; then
    echo they are equal
else
    echo they are not equal
fi

O corta el último \ren myvar:

if [ "${myvar%$'\r'*}" = "hello" ]; then
    echo they are equal
else
    echo they are not equal
fi
yaegashi
fuente
3

Curiosamente, en muchos shells getoptses un candidato muy probable para un trabajo como este. Esto puede parecer contradictorio al principio, pero si considera que getopts'la función principal es reconocer y ofrecer para la interpretación tantas opciones de línea de comandos de un solo carácter especificadas como se pueden encontrar en una serie concatenada de las mismas, podría comenzar a hacer un poco mas sentido.

Para demostrar, desde un bashshell:

x=$(printf '\n\r%010s\t' hello)
OPTIND=1
while  getopts : na "-$x"
do     printf %q\\n "$OPTARG"
done

$'\n'
$'\r'
\
\
\
\
\
h
e
l
l
o
$'\t'

De esa manera, a veces puede ser conveniente permitir getoptsmanejar el desmontaje como una especie de piloto automático de shell para casos como este. Cuando lo haga, puede eliminar los bytes no deseados w / a caseo [probar ]y crear una copia de seguridad de su cadena desde el byte 1:

OPTIND=1 y=$(printf \\n\\r) z=
while  getopts : na "-$x"
do     case $OPTARG in ([!$y])
            z=$z$OPTARG
       esac
done
printf %q\\n "$z"

$'     hello\t'

Dado este simple caso de ejemplo, y dado un shell que admite las expansiones de parámetros ya mencionadas en otra parte, dichas expansiones probablemente le servirán mejor aquí. Pero pensé getoptsque valdría la pena mencionarlo también en caso de que no conocieras sus capacidades a este respecto. Ciertamente, cuando me enteré, encontré muchas aplicaciones útiles para ello, de todos modos.

mikeserv
fuente
0

Si bien Bash y otros lenguajes de shell son útiles, a veces es mejor usar un lenguaje de script verdadero, como Perl. Perl puede reemplazar scripts de shell que llaman a otros lenguajes como sed y awk, así como comandos UNIX con bastante facilidad. Aprendí esto hace más de 20 años cuando escribía scripts de C-Shell que a su vez llamaban sed, awk y varios comandos UNIX, antes de llamar al código FORTRAN. En Perl haría:

chomp($myvar);   # removes the newline char

if("$myvar" eq "hello")   # string comparison
  {
  print "they are equal\n";
  }
else
  {
  print "they are not equal\n";
  }
Peter
fuente