Mengapa -a di “#! / Bin / sh -a” memengaruhi sed dan “set -a” tidak?

20

Jika saya menjalankan file .sh berikut:

#!/bin/sh -a
echo "a" | sed -e 's/[\d001-\d008]//g'

Hasilnya adalah kesalahan:

sed: -e express # 1, char 18: End range tidak valid

Tetapi jika saya menjalankan file .sh berikut:

#!/bin/sh
set -a
echo "a" | sed -e 's/[\d001-\d008]//g'

Ini berjalan tanpa kesalahan. Bukankah kode kedua seharusnya sama dengan yang pertama? Mengapa kesalahan yang pertama?

Rodrigo
sumber
Tidak semua shsama. Tidak semua sed setara. Yang shkamu gunakan Di OS mana? dan yang sed (mungkin? sed --versionjika tidak gagal)?
Isaac
1
pengaturan LC_COLLATE=C(atau POSIX) untuk panggilan untuk sedmengatasi masalah
Jeff Schaller
4
Satu perbedaan yang saya temukan: skrip pertama memanggil sed (dan mungkin ada utilitas lain) dengan POSIXLY_CORRECT=ydi lingkungan, yang kedua tidak ada POSIXLY_CORRECTdi lingkungan. Shell saya memanggil kedua skrip dari tidak ada POSIXLY_CORRECTdi lingkungannya.
Mark Plotnick
1
Ah, echo "a" | POSIXLY_CORRECT=y sed -e 's/[\d001-\d008]//g' mereproduksi masalah Anda
Isaac
1
Mengkonfirmasi bahwa hal di atas gagal untuk saya persis seperti yang ditunjukkan OP pada CentOS 7.x - GNU bash, versi 4.2.46 (2) -release (x86_64-redhat-linux-gnu) & CentOS Linux rilis 7.5.1804 (Core) .
slm

Jawaban:

31

Ketika bash dipanggil dengan namanya sh, ia melakukan ini :

if (shell_name[0] == 's' && shell_name[1] == 'h' && shell_name[2] == '\0')
    act_like_sh++;

dan kemudian mengatur POSIXLY_CORRECTvariabel shell key :

if (act_like_sh)
  {
    bind_variable ("POSIXLY_CORRECT", "y", 0);
    sv_strict_posix ("POSIXLY_CORRECT");
  }

bind_variablepanggilan bind_variable_internal, yang, jika atribut shell aaktif pada saat itu (yang akan terjadi jika Anda memanggil shell dengan -a), menandai variabel shell sebagai diekspor .

Jadi dalam skrip pertama Anda:

#!/bin/sh -a
echo "a" | sed -e 's/[\d001-\d008]//g'

seddipanggil dengan POSIXLY_CORRECT=ydi lingkungannya, yang akan membuatnya mengeluh [\d001-\d008]. (Hal yang sama terjadi jika sed diberikan --posixopsi.)

Di GNU sed, adalah kode melarikan diri untuk karakter yang nilai numerik dalam basis-10 adalah NNN , tapi dalam modus POSIX, ini dinonaktifkan dalam ekspresi braket, sehingga , berarti harfiah karakter , dll, dengan kisaran yang dari untuk . Dalam urutan kode karakter, muncul sebelum (dan rentang mencakup semua digit kecuali nol, ditambah semua huruf besar, ditambah beberapa karakter khusus). Di lokal yang Anda gunakan, ketik sebelumnya , sehingga rentang tidak valid.\dNNN[\d001-\d008]\d1\1\en_US.UTF-8\1

Dalam skrip kedua Anda:

#!/bin/sh
set -a
echo "a" | sed -e 's/[\d001-\d008]//g'

meskipun POSIXLY_CORRECTdiatur dalam shell, itu tidak diekspor, jadi sed dipanggil tanpa POSIXLY_CORRECTdi lingkungan, dan sed berjalan dengan ekstensi GNU.

Jika Anda menambahkan di export POSIXLY_CORRECTdekat bagian atas skrip kedua, Anda juga akan melihat keluhan.

Tandai Plotnick
sumber
6
Bagi saya, itu bug.
Stéphane Chazelas
1
horor shell suci, Batman! Itu adalah kekhasan yang menarik (dan sedikit perubahan untuk melihat masalah yang /bin/shsebenarnya berasal dari menjadi Bash). Hal yang sama terjadi jika POSIXLY_CORRECTada di lingkungan sebelum shBash dimulai: ia juga akan meneruskannya sebagai POSIXLY_CORRECT=y.
ilkkachu
3
@ Svenvenenny, tetapi POSIXLY_CORRECT tidak di lingkungan ketika shell dimulai, dan skrip tidak mengaturnya. Shell tidak. Itu menciptakan variabel lingkungan entah dari mana, yang lebih buruk karena melakukan itu dalam mode di mana seharusnya, dan mencoba untuk menjadi standar-compliant.
ilkkachu
4
FWIW, Bash juga tampaknya tidak mendokumentasikan bahwa itu akan diatur POSIXLY_CORRECTsendiri. Tidak disebutkan dalam daftar efek mode POSIX dan deskripsi variabel hanya mengatakan bahwa pengaturan itu mengubah shell ke mode POSIX, bukan sebaliknya.
ilkkachu
1
@ilkkachu. Selesai Saya pikir spec POSIX juga harus diperbarui untuk memperjelas variabel apa yang dipengaruhi oleh allexport.
Stéphane Chazelas