So behaupten Sie, dass eine Zeichenfolge ein Zeilenumbruchzeichen hat, und entfernen Sie sie gegebenenfalls

9

Ich habe eine Zeichenfolge, die das Ergebnis einer Operation ist, über die ich keine Kontrolle habe. Wenn ich diese Variable mit drucke echo, erhalte ich:

echo $myvar
hello

Wenn ich es jedoch tue

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

Ich verstehe immer, dass sie nicht gleich sind. Ich vermute, das liegt an einem newlineCharakter.

Die Zeichenfolge verhält sich auch seltsam. Wenn ich es tue:

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

Ich bekomme:

hellois my var twice: hello

Wie kann ich überprüfen, ob dies tatsächlich auf a zurückzuführen ist, newlineund es gegebenenfalls entfernen?

farid99
quelle
1
In Bash können printf '%q\n' "$string"Sie eine Escape-Version einer beliebigen Zeichenfolge abrufen. Zum Beispiel: printf '%q\n' 'foo\n'-> foo\\n; printf '%q\n' $'foo\n'->$'foo\n'
10b0
1
Sie zitieren nicht die Erweiterung einer Ihrer Variablen. Wenn sie ein nachfolgendes Leerzeichen hätten, würden Sie es nicht sehen echo $foo. Tun Sie echo "$foo"stattdessen.
Peter Cordes

Antworten:

9

Das Problem ist, dass Sie einen eingebetteten Carriage-Return (CR, \r) haben. Dies führt dazu, dass die Texteinfügemarke des Terminals an den Anfang der zu druckenden Zeile zurückgesetzt wird. Aus diesem Grund sehen Sie in Ihrem $newVARBeispiel das "Hallo" am Anfang der Zeile - sed -n lzeigt eine lesbare Ansicht der nicht druckbaren Zeichen (und des Zeilenende) an.

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

Sie können dies mit einer einfachen Bash-Bedingungsprüfung testen:

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

Sie können den Test und die Korrektur in einem Schritt kombinieren, indem Sie auf \r(s) testen und diese entfernen über:

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

Das Update verwendet die Shell-Parametererweiterung . Die oben verwendete spezielle Form dient zum Ersetzen von Teilzeichenfolgen basierend auf Ihrem angegebenen Muster: ${parameter/pattern/string}<- Dies ersetzt nur das zuerst gefundene Muster durch eine Zeichenfolge in der Variablen * parameter. Um alle Muster zu ersetzen , müssen Sie nur das erste /in ändern //.

Peter.O
quelle
Könntest du dein letztes Stück Code erklären? die fix="....Linie?
farid99
@ farid99: Erklärung zur Antwort hinzugefügt, Hinweis fixkann sich varselbst sein - oder oft können Sie einfach die Parametererweiterung
unverändert verwenden,
5

Sie können \rwie $'\r'in Bash darstellen:

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

Oder hacken Sie den letzten \rin myvar:

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

Seltsamerweise ist in vielen Muscheln getoptsein sehr wahrscheinlicher Kandidat für einen Job wie diesen. Dies mag zunächst nicht intuitiv erscheinen, aber wenn Sie bedenken, dass getoptsdie Hauptfunktion darin besteht, so viele bestimmte Befehlszeilenoptionen für einzelne Zeichen zu erkennen und zur Interpretation anzubieten, wie sie in einer verketteten Reihe derselben enthalten sind, kann dies zu einer gewissen Beeinträchtigung führen mehr Sinn.

Um zu demonstrieren, aus einer 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'

Auf diese Weise kann es manchmal zweckmäßig sein, getoptsdie Demontage in solchen Fällen als eine Art Shell-Autopilot zu handhaben. Wenn Sie dies tun, können Sie einfach unerwünschte Bytes mit a aussortieren caseoder Ihre String-Sicherung aus Byte 1 [testen ]und erstellen:

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'

In Anbetracht dieses einfachen Beispielfalls - und einer Shell, die die bereits an anderer Stelle erwähnten Parametererweiterungen unterstützt - werden diese Erweiterungen Ihnen hier wahrscheinlich besser dienen. Aber ich dachte, es getoptskönnte auch eine Erwähnung wert sein, falls Sie sich dessen in dieser Hinsicht nicht bewusst sind. Als ich davon erfuhr, fand ich jedenfalls viele nützliche Anwendungen dafür.

mikeserv
quelle
0

Während Bash und andere Shell-Sprachen praktisch sind, ist es manchmal besser, eine echte Skriptsprache wie Perl zu verwenden. Perl kann Shell-Skripte, die andere Sprachen wie sed und awk aufrufen, sowie UNIX-Befehle ganz einfach ersetzen. Ich habe dies vor über 20 Jahren gelernt, als ich C-Shell-Skripte schrieb, die wiederum sed, awk und verschiedene UNIX-Befehle aufriefen - bevor ich FORTRAN-Code aufrief. In Perl würde ich tun:

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
quelle