Shell: Memeriksa apakah sebuah string berisi karakter tertentu [duplikat]
#!/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
Masalah utama Anda adalah Anda menggunakan kecocokan pola [[ ... ]]
dalam skrip yang dieksekusi /bin/sh
yang 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 ls
menjadi 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 Folder
direktori yang memiliki huruf b
di dalamnya, dan kemudian mencetak file yang tidak berisi file a
. Tes a
karakter dilakukan dengan menggunakan case ... esac
. The *a*
Pola cocok dengan a
karakter dalam nama file jika ada satu, dalam hal tidak ada yang terjadi. The printf
pernyataan dalam "kasus default" yang dijalankan jika tidak ada a
dalam string.
Tes dengan -e
dan -L
ada 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 -L
tes 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 bash
kode di bawah ini tidak perlu ini karena menggunakan nullglob
opsi shell bukan (tidak tersedia di /bin/sh
).
Menjalankan loop pada perluasan *b*
pola alih-alih ls
output 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 bash
shell, 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?
Untuk mencetak nama file Folder
yang berisi b
dan tidak a
, dalam zsh
dan ~
( 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 sh
sintaks standar ) dan operatornya &
( dan ) dan !(...)
( bukan ):
#! /bin/ksh93 -
(
CDPATH= cd Folder &&
set -- ~(N)@(*b*&!(*a*)) &&
(($# > 0)) && printf '%s\n' "$@"
)
Dengan bash
shell (yang suka zsh
juga telah disalin ksh ini [[...]]
operator), menggunakan extglob
untuk mendukung ksh88 operator gumpal dan nullglob
untuk mendapatkan efek zsh ini N
kualifikasi 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 sh
sintaks 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 cd
tidak akan berfungsi jika Anda memiliki akses baca Folder
tetapi 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 case
konstruksi 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