Shell: Überprüfen, ob eine Zeichenfolge ein bestimmtes Zeichen enthält [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

Ich habe versucht, die Namen auszugeben, die kein 'a', sondern 'b' enthalten. Dies ist der Fehler, den ich bekommen habe:

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

Was mache ich hier falsch?

Antworten

5 Kusalananda Dec 22 2020 at 02:19

Ihr Hauptproblem besteht darin, dass Sie eine Musterübereinstimmung [[ ... ]]in einem Skript verwenden, das von /bin/shdiesen Testtypen nicht unterstützt wird. Siehe auch die Frage mit dem Titel Shell-Skript löst beim Ausführen aus einer sh-Datei einen nicht gefundenen Fehler aus. Bei manueller Eingabe funktionieren die Befehle jedoch

Das andere Problem besteht darin, dass Sie die Ausgabe von lszu einer einzelnen Zeichenfolge kombinieren, bevor Sie sie in Wörter auf Leerzeichen, Tabulatoren und Zeilenumbrüchen aufteilen und den generierten Wörtern Dateinamen-Globbing zuweisen. Sie durchlaufen dann das Ergebnis.

Stattdessen:

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

Dies durchläuft alle Dateien im FolderVerzeichnis, in denen sich der Buchstabe befindet b, und druckt dann diejenigen aus, die keine enthalten a. Der Test für den aCharakter wird mit durchgeführt case ... esac. Das *a*Muster stimmt mit einem aZeichen im Dateinamen überein, falls es eines gibt. In diesem Fall passiert nichts. Die printfAnweisungen befinden sich im "Standardfall", der ausgeführt wird, wenn adie Zeichenfolge keine enthält .

Die Tests mit -eund -Lsollen sicherstellen, dass wir den Randfall erfassen, in dem das Schleifenmuster mit nichts übereinstimmt. In diesem Fall bleibt das Muster nicht erweitert. Um sicherzustellen, dass wir zwischen einem nicht erweiterten Muster und einer tatsächlich vorhandenen Datei unterscheiden können, testen wir mit -e. Der -LTest besteht darin, den weiteren Randfall einer symbolischen Verknüpfung zu erfassen, die denselben Namen wie das Schleifenmuster hat, jedoch nicht auf eine gültige Stelle verweist. Der folgende bashCode benötigt dies nicht, da nullglobstattdessen die Shell-Option verwendet wird (in nicht verfügbar /bin/sh).

Das Ausführen der Schleife über die Erweiterung des *b*Musters anstelle der lsAusgaben hat den Vorteil, dass auch Dateinamen mit Leerzeichen, Tabulatoren, Zeilenumbrüchen und Dateinamen-Globbing-Zeichen verarbeitet werden können (die Dateinamen werden niemals in eine einzelne Zeichenfolge eingefügt, die wir dann versuchen iterieren über).

In der bashShell können Sie dasselbe mit einem [[ ... ]]Mustervergleichstest tun, wie Sie es selbst versucht haben:

#!/bin/bash

cd Folder || exit 1

shopt -s nullglob

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

Siehe auch:

  • Warum verschluckt sich mein Shell-Skript an Leerzeichen oder anderen Sonderzeichen?
  • Wann ist eine doppelte Anführungszeichen erforderlich?
  • Warum * nicht * `ls` analysieren (und was stattdessen tun)?
  • Warum ist printf besser als echo?
6 StéphaneChazelas Dec 22 2020 at 02:35

So drucken Sie die Dateinamen Folder, die enthalten bund nicht a, in zshund seinen ~( außer / und-nicht ) Glob-Operator:

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

(Entfernen Sie das N(für nullglob, wenn Sie lieber einen Fehler melden möchten, wenn keine passende Datei vorhanden ist).

In ksh93(der Shell, die diesen [[...]]Operator eingeführt hat (der nicht Teil der Standardsyntax ist sh) und seinen &( und ) und !(...)( nicht ) Operatoren:

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

Mit der bashSchale (die gerne zshauch kshs kopiert [[...]]Operator) unter Verwendung von extglobksh88 glob Betreiber zu unterstützen und nullglobdie Wirkung von zsh der bekommen Nglob Qualifier oder ksh93‚s ~(N)glob Operator und eine doppelte Verneinung mit |( oder ) zu erreichen , und :

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

In der Standardsyntax sh:

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

Beachten Sie, dass die verwendeten Lösungen cdnicht funktionieren, wenn Sie Lesezugriff darauf haben, Folderaber keinen Suchzugriff darauf haben.

Um die allgemeine Frage zu beantworten, wie überprüft werden kann, ob eine Zeichenfolge eine andere enthält sh, ist es am besten, das caseKonstrukt zu verwenden , mit dem Sie den Mustervergleich durchführen sh. Sie könnten sogar eine Hilfsfunktion verwenden:

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

So zu verwenden, als ob:

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