Shell: Memeriksa apakah sebuah string berisi karakter tertentu [duplikat]

Dec 22 2020
#!/bin/sh

TMP=$(cd Folder && ls) for name in $TMP; do

  if [[ "${name}" != *"a"* -a ${name} == *"b"* ]] ;then
   echo $name
  fi

done

Saya mencoba mengeluarkan nama yang tidak memiliki 'a' di dalamnya tetapi 'b' sebagai gantinya. Ini adalah kesalahan yang saya dapatkan:

./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found

Apa yang saya lakukan salah di sini?

Jawaban

5 Kusalananda Dec 22 2020 at 02:19

Masalah utama Anda adalah Anda menggunakan kecocokan pola [[ ... ]]dalam skrip yang dieksekusi /bin/shyang tidak mendukung jenis pengujian ini. Lihat juga pertanyaan berjudul Skrip shell melempar kesalahan yang tidak ditemukan saat dijalankan dari file sh. Tetapi jika dimasukkan secara manual perintah berfungsi

Masalah lainnya adalah Anda menggabungkan output dari lsmenjadi satu string sebelum membaginya menjadi kata-kata pada spasi, tab dan baris baru, dan menerapkan penggabungan nama file ke kata-kata yang dihasilkan. Anda kemudian mengulang hasil ini.

Sebagai gantinya:

#!/bin/sh

cd Folder || exit 1

for name in *b*; do
    [ -e "$name" ] || [ -L "$name" ] || continue
    case $name in (*a*) ;; (*) printf '%s\n' "$name"; esac
done

Ini mengulang semua file di Folderdirektori yang memiliki huruf bdi dalamnya, dan kemudian mencetak file yang tidak berisi file a. Tes akarakter dilakukan dengan menggunakan case ... esac. The *a*Pola cocok dengan akarakter dalam nama file jika ada satu, dalam hal tidak ada yang terjadi. The printfpernyataan dalam "kasus default" yang dijalankan jika tidak ada adalam string.

Tes dengan -edan -Lada untuk memastikan bahwa kami menangkap kasus tepi di mana pola loop tidak cocok dengan apapun. Dalam hal ini, pola akan tetap tidak diperluas, jadi untuk memastikan bahwa kita dapat membedakan antara pola yang tidak diperluas dan file yang sebenarnya ada, kita uji dengan -e. The -Ltes adalah untuk menangkap kasus tepi lebih lanjut dari symbolic link yang memiliki nama yang sama dengan pola lingkaran, tapi itu tidak menunjuk ke mana saja berlaku. The bashkode di bawah ini tidak perlu ini karena menggunakan nullglobopsi shell bukan (tidak tersedia di /bin/sh).

Menjalankan loop pada perluasan *b*pola alih-alih lsoutput apa pun memiliki manfaat bahwa ini juga dapat menangani nama file yang berisi spasi, tab, baris baru, dan karakter globbing nama file (nama file tidak pernah dimasukkan ke dalam satu string yang kemudian kami coba ulangi).

Di bashshell, Anda dapat melakukan hal yang sama dengan [[ ... ]]pengujian pencocokan pola, seperti yang Anda coba lakukan sendiri:

#!/bin/bash

cd Folder || exit 1

shopt -s nullglob

for name in *b*; do
    [[ $name != *a* ]] && printf '%s\n' "$name"
done

Lihat juga:

  • Mengapa skrip shell saya tersedak spasi atau karakter khusus lainnya?
  • Kapan kutipan ganda diperlukan?
  • Mengapa * tidak * mengurai `ls` (dan apa yang harus dilakukan)?
  • Mengapa printf lebih baik daripada echo?
6 StéphaneChazelas Dec 22 2020 at 02:35

Untuk mencetak nama file Folderyang berisi bdan tidak a, dalam zshdan ~( kecuali / dan tidak ) operator globnya:

#! /bin/zsh -
set -o extendedglob
print -rC1 -- Folder/(*b*~*a*)(N:t)

(hapus N(untuk nullglob, jika Anda lebih suka kesalahan dilaporkan di mana tidak ada file yang cocok).

Dalam ksh93(shell yang memperkenalkan [[...]]operator itu (yang bukan bagian dari shsintaks standar ) dan operatornya &( dan ) dan !(...)( bukan ):

#! /bin/ksh93 -
(
  CDPATH= cd Folder &&
    set -- ~(N)@(*b*&!(*a*)) &&
    (($# > 0)) && printf '%s\n' "$@"
)

Dengan bashshell (yang suka zshjuga telah disalin ksh ini [[...]]operator), menggunakan extglobuntuk mendukung ksh88 operator gumpal dan nullglobuntuk mendapatkan efek zsh ini Nkualifikasi gumpal atau ksh93's ~(N)Operator gumpal dan beberapa negasi ganda dengan |( atau ) untuk mencapai dan :

#! /bin/bash -
(
  shopt -s extglob nullglob
  cd ./Folder &&
    set -- !(!(*b*)|*a*) &&
    (($# > 0)) && printf '%s\n' "$@"
)

Dalam shsintaks standar :

#! /bin/sh -
(
  CDPATH= cd Folder || exit
  set -- [*]b[*] *b*
  [ "$1/$2" = '[*]b[*]/*b*' ] && exit # detect the case where the pattern
                                      # expands to itself because there's
                                      # no match as there's no nullglob
                                      # equivalent in sh
  shift
  for i do
    case $i in (*a*) ;; (*) set -- "$@" "$i" esac shift done [ "$#" -gt 0 ] && printf '%s\n' "$@"
)

Perhatikan bahwa solusi yang digunakan cdtidak akan berfungsi jika Anda memiliki akses baca Foldertetapi tidak mencari akses ke sana.

Secara lebih umum, untuk menjawab pertanyaan umum tentang bagaimana memeriksa apakah sebuah string berisi string lain sh, yang terbaik adalah dengan casekonstruksi yang merupakan cara Anda melakukan pencocokan pola sh. Anda bahkan dapat menggunakan fungsi pembantu:

contains()
  case "$1" in
    (*"$2"*) true;;
    (*) false;;
  esac

Untuk digunakan seolah-olah:

if contains foobar o; then
  echo foobar contains o
fi
if ! contains foobar z; then
  echo foobar does not contain o
fi
if
  contains "$file" b &&
    ! contains "$file" a then printf '%s\n' "$file contains b and not a "
fi