Cara menegaskan bahwa string memiliki karakter baris baru dan, jika demikian, hapus

9

Saya memiliki string yang merupakan hasil dari beberapa operasi yang tidak dapat saya kendalikan. Ketika saya mencetak variabel ini menggunakan echo, saya mendapatkan:

echo $myvar
hello

Namun, ketika saya melakukannya

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

Saya selalu mendapatkan bahwa mereka tidak setara. Saya menduga ini karena newlinekarakter.

Tali juga bertingkah aneh. Ketika saya melakukannya:

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

Saya mendapat:

hellois my var twice: hello

Bagaimana saya bisa memeriksa apakah ini disebabkan oleh newlinedan, jika demikian, hapus?

farid99
sumber
1
Di Bash Anda bisa printf '%q\n' "$string"mendapatkan versi apa pun dari string apa pun. Misalnya: printf '%q\n' 'foo\n'-> foo\\n; printf '%q\n' $'foo\n'->$'foo\n'
l0b0
1
Anda tidak mengutip perluasan dari salah satu variabel Anda. Jika mereka memiliki spasi spasi tambahan, Anda tidak akan melihatnya echo $foo. Lakukan echo "$foo"sebaliknya.
Peter Cordes

Jawaban:

9

Masalahnya adalah Anda memiliki Carriage-Return (CR, \r) yang tertanam . Hal ini menyebabkan titik penyisipan teks terminal untuk menendang kembali ke awal baris yang sedang dicetak. Itulah sebabnya Anda melihat 'halo' di awal baris dalam $newVARcontoh Anda - sed -n lmenampilkan tampilan yang dapat dibaca dari karakter yang tidak diinginkan (dan akhir baris).

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

Anda dapat mengujinya dengan cek kondisi bash sederhana:

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

Anda dapat menggabungkan pengujian dan memperbaikinya dalam satu langkah dengan menguji untuk \rdan menghapusnya melalui:

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

Cara mengatasinya menggunakan Shell Parameter Expansion . Bentuk khusus yang digunakan di atas adalah untuk mengganti substring berdasarkan pola yang Anda buat: ${parameter/pattern/string}<- Ini hanya menggantikan pola yang ditemukan pertama kali dengan string dalam variabel bernama * parameter. Untuk mengganti semua pola, Anda hanya perlu mengubah yang pertama /untuk //.

Peter.O
sumber
Bisakah Anda jelaskan sedikit kode terakhir? yang fix="....garis?
farid99
@ farid99: penjelasan ditambahkan ke jawaban, Note fixdapat dengan varsendirinya - atau seringkali Anda hanya dapat menggunakan ekspansi parameter apa adanya tanpa perlu menetapkan ulang nilai (mungkin) yang dimodifikasi ..
Peter.O
5

Anda dapat mewakili \rseperti $'\r'dalam bash:

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

Atau potong yang terakhir \rdi myvar:

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

Anehnya, dalam banyak cangkang getoptsadalah kandidat yang sangat mungkin untuk pekerjaan seperti ini. Ini mungkin tampak berlawanan dengan intuisi pada awalnya, tetapi jika Anda menganggap bahwa getopts'fungsi utama adalah untuk mengenali dan menawarkan untuk penafsiran sebanyak opsi opsi baris perintah karakter tunggal yang ditentukan seperti yang dapat ditemukan dalam serangkaian yang sama, mungkin mulai membuat sedikit lebih masuk akal.

Untuk menunjukkan, dari 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'

Dengan cara itu kadang-kadang bisa lebih mudah untuk memungkinkan getoptsmenangani pembongkaran sebagai semacam pilot otomatis shell untuk kasus-kasus seperti ini. Ketika Anda melakukannya, Anda bisa menyaring byte yang tidak diinginkan dengan caseatau [menguji ]dan membangun kembali string Anda dari 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'

Dengan contoh kasus sederhana ini - dan diberi shell yang mendukung ekspansi parameter yang telah disebutkan di tempat lain - mengatakan ekspansi mungkin akan melayani Anda lebih baik di sini. Tapi saya pikir getoptsmungkin layak disebut juga jika Anda tidak menyadari kemampuannya dalam hal ini. Tentu saja ketika saya mengetahuinya, saya menemukan banyak aplikasi yang berguna untuk itu.

mikeserv
sumber
0

Meskipun Bash dan bahasa shell lainnya berguna, terkadang lebih baik menggunakan bahasa skrip yang sebenarnya - seperti Perl. Perl dapat mengganti skrip shell yang memanggil bahasa lain seperti sed dan awk serta perintah UNIX dengan mudah. Saya belajar ini 20+ tahun yang lalu ketika menulis skrip C-Shell yang pada gilirannya disebut sed, awk, dan berbagai perintah UNIX - sebelum memanggil kode FORTRAN. Dalam Perl saya akan melakukan:

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
sumber