Kabuk: Bir dizenin belirtilen bir karakteri [yinelenen] içerip içermediğini kontrol etme

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

İçinde 'a' olmayan ama onun yerine 'b' olan isimleri çıkarmaya çalışıyordum. Aldığım hata bu:

./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

Burada neyi yanlış yapıyorum?

Yanıtlar

5 Kusalananda Dec 22 2020 at 02:19

Ana sorununuz, bu tür testleri desteklemeyen [[ ... ]]bir komut dosyasında bir model eşleştirmesi kullanmanızdır /bin/sh. Ayrıca, bir sh dosyasından çalıştırıldığında, Kabuk betiği, bulunamadı hatası atıyor başlıklı soruya bakın . Ancak manuel olarak girilirse komutlar çalışır

Diğer sorun ise, çıktısını lsboşluklar, sekmeler ve satırsonları üzerindeki kelimelere ayırmadan önce tek bir dizede birleştirmeniz ve oluşturulan kelimelere dosya adı globbing uygulamanızdır. Daha sonra bunun sonucunun üzerinden geçersiniz.

Yerine:

#!/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

Bu, Folderdizindeki içinde harf bbulunan tüm dosyalar üzerinde döngü yapar ve sonra bunlardan a. aKarakterin testi kullanılarak yapılır case ... esac. *a*Desen bir maçları avaka hiçbir şey olmuyor ki, eğer varsa dosya adını karakter. printfİfadeleri hiçbir yoksa işletilirse, "varsayılan" durumda olduğu adizesinde.

Döngü deseninin hiçbir şeyle eşleşmediği uç durumu yakaladığımızdan emin olmak için -eve ile yapılan testler -Lvar. Bu durumda, model genişletilmemiş olarak kalacaktır, bu nedenle genişletilmemiş bir model ile mevcut bir dosya arasında ayrım yapabildiğimizden emin olmak için test ederiz -e. -LTest döngü deseni ile aynı ada sahip bir sembolik bağın daha da kenar durumda yakalamak, ama bu her yerde geçerli işaret etmez. Aşağıdaki bashkodun buna ihtiyacı yoktur çünkü onun nullglobyerine kabuk seçeneğini kullanır (içinde mevcut değildir /bin/sh).

Döngünün çıktılar *b*yerine örgünün genişletilmesi üzerinden çalıştırılması, bunun lsaynı zamanda boşluklar, sekmeler, satırsonları ve dosya adı genelleme karakterleri içeren dosya adlarıyla da başa çıkabilme avantajına sahiptir (dosya adları asla daha sonra denediğimiz tek bir dizeye yerleştirilmez) tekrar tekrar).

In bashkabuk, bir ile aynı yapabileceğini [[ ... ]]kendiniz yapmaya çalıştık gibi, desen eşleştirme testi:

#!/bin/bash

cd Folder || exit 1

shopt -s nullglob

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

Ayrıca bakınız:

  • Kabuk betiğim neden boşlukta veya diğer özel karakterlerde boğuluyor?
  • Çift tırnak ne zaman gereklidir?
  • "Ls" yi neden * çözümlemiyor * (ve bunun yerine ne yapmalı)?
  • Printf neden echo'dan daha iyidir?
6 StéphaneChazelas Dec 22 2020 at 02:35

Dosya adlarını yazdırmak için Folderiçermez bve aiçinde zshve onun ~( hariç / ve-olmayan ) glob operatörü:

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

(kaldırmak Niçin ( nullglobBunun yerine eşleşen dosya olduğu yerde bir hata bildirilebilir istediğimiz ise).

In ksh93(bu [[...]]operatörü (standart shsözdiziminin parçası olmayan ) ve &( ve ) ve !(...)( değil ) operatörlerini tanıtan kabuk :

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

İle bash(ister kabuk zshayrıca Ksh en kopyaladıysa [[...]]kullanarak operatörü), extglobksh88 glob operatörlerini desteklemek ve nullglobzsh en etkisini elde etmek için Nglob eleme veya ksh93'ın ~(N)glob operatörü ve bazı çift olumsuzlamasıydı |( veya elde etmek) ve :

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

Standart shsözdiziminde:

#! /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' "$@"
)

cdOkuma erişiminiz varsa Folderancak arama erişiminiz yoksa , kullanılan çözümlerin çalışmayacağını unutmayın .

Daha genel olarak, bir dizenin başka bir dizge içerip içermediğinin nasıl kontrol edileceğine ilişkin genel soruyu yanıtlamak için sh, en iyisi, içinde casedesen eşleştirmeyi nasıl yaptığınız yapıdadır sh. Bir yardımcı işlev bile kullanabilirsiniz:

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

Gibi kullanmak için:

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