Jak zapewnić, że ciąg ma znak nowej linii, a jeśli tak, usuń go

9

Mam ciąg będący wynikiem operacji, nad którą nie mam kontroli. Kiedy drukuję tę zmienną za pomocą echo, otrzymuję:

echo $myvar
hello

Jednak kiedy to zrobię

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

Zawsze rozumiem, że nie są równi. Podejrzewam, że dzieje się tak z powodu newlinepostaci.

Sznurek zachowuje się również dziwnie. Kiedy robię:

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

Dostaję:

hellois my var twice: hello

Jak mogę sprawdzić, czy jest to faktycznie spowodowane, newlinea jeśli tak, to go usunąć?

farid99
źródło
1
W Bash możesz printf '%q\n' "$string"uzyskać ucieczkową wersję dowolnego łańcucha. Na przykład: printf '%q\n' 'foo\n'-> foo\\n; printf '%q\n' $'foo\n'->$'foo\n'
l0b0
1
Nie cytujesz rozszerzenia żadnej ze swoich zmiennych. Gdyby mieli jakieś końcowe białe znaki, nie zobaczyłbyś tego echo $foo. Rób echo "$foo"zamiast tego.
Peter Cordes,

Odpowiedzi:

9

Problem polega na tym, że masz osadzony Carriage-Return (CR, \r). Powoduje to, że punkt wstawiania tekstu terminala wraca do początku linii, którą drukuje. Dlatego właśnie widzisz „witaj” na początku wiersza w swoim $newVARprzykładzie - sed -n lwyświetla czytelny widok znaków niedrukowalnych (i końca wiersza).

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

Możesz to sprawdzić za pomocą prostej kontroli warunku bash:

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

Możesz połączyć test i naprawić w jednym kroku, testując \r(e) i usuwając je poprzez:

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

Fix wykorzystuje Shell parametrów Expansion . Szczególna forma używana powyżej jest do wymiany podciągi oparte na proovided Wzór: ${parameter/pattern/string}<- tylko pierwszy znalezionych Zastępuje wzór ze sznurkiem w zmiennej o nazwie * parametr. Aby zastąpić wszystkie wzorce, wystarczy zmienić pierwszy /na //.

Peter.O
źródło
czy mógłbyś wyjaśnić swój ostatni kawałek kodu? fix="....linia?
farid99
@ farid99: wyjaśnienie dodane do odpowiedzi, Uwaga fixmoże być varsama - lub często możesz po prostu użyć rozszerzenia parametrów bez zmian, bez konieczności ponownego przypisywania (ewentualnie) zmodyfikowanej wartości.
Peter.O
5

Możesz reprezentować \rjak $'\r'w bash:

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

Lub posiekaj ostatni \rw myvar:

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

Co ciekawe, w wielu muszlach getoptsjest bardzo prawdopodobnym kandydatem na taką pracę. Na początku może się to wydawać sprzeczne z intuicją, ale jeśli weźmiesz pod uwagę, że getopts„podstawową funkcją jest rozpoznawanie i oferowanie do interpretacji tylu określonych opcji wiersza poleceń, które można znaleźć w połączonej serii takich samych, to może zacząć robić trochę więcej rozsądku.

Aby zademonstrować, z bashpowłoki:

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'

W ten sposób czasami może być wygodne umożliwienie getoptsobsługi demontażu jako rodzaju automatycznego pilota powłoki dla takich przypadków. Gdy to zrobisz, możesz po prostu odfiltrować niechciane bajty w / a caselub [przetestować ]i zbudować kopię zapasową ciągu od bajtu 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'

Biorąc pod uwagę ten prosty przykładowy przypadek - i powłokę, która obsługuje rozszerzenia parametrów już wspomniane gdzie indziej - wspomniane rozszerzenia zapewnią ci lepszą obsługę. Ale pomyślałem, getoptsże warto również wspomnieć, na wypadek gdybyś nie był świadomy jego możliwości w tym zakresie. Z pewnością, kiedy się o tym dowiedziałem, i tak znalazłem wiele przydatnych aplikacji.

mikeserv
źródło
0

Podczas gdy Bash i inne języki powłoki są przydatne, czasem lepiej jest użyć prawdziwego języka skryptowego - takiego jak Perl. Perl może dość łatwo zastępować skrypty powłoki, które wywołują inne języki, takie jak sed i awk, a także polecenia UNIX. Nauczyłem się tego ponad 20 lat temu, pisząc skrypty C-Shell, które z kolei wywoływały sed, awk i różne komendy UNIX - przed wywołaniem kodu FORTRAN. W Perlu zrobiłbym:

chomp($myvar);   # removes the newline char

if("$myvar" eq "hello")   # string comparison
  {
  print "they are equal\n";
  }
else
  {
  print "they are not equal\n";
  }
Piotr
źródło