Shell: Überprüfen, ob eine Zeichenfolge ein bestimmtes Zeichen enthält [Duplikat]
#!/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
Ihr Hauptproblem besteht darin, dass Sie eine Musterübereinstimmung [[ ... ]]
in einem Skript verwenden, das von /bin/sh
diesen 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 ls
zu 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 Folder
Verzeichnis, in denen sich der Buchstabe befindet b
, und druckt dann diejenigen aus, die keine enthalten a
. Der Test für den a
Charakter wird mit durchgeführt case ... esac
. Das *a*
Muster stimmt mit einem a
Zeichen im Dateinamen überein, falls es eines gibt. In diesem Fall passiert nichts. Die printf
Anweisungen befinden sich im "Standardfall", der ausgeführt wird, wenn a
die Zeichenfolge keine enthält .
Die Tests mit -e
und -L
sollen 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 -L
Test 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 bash
Code benötigt dies nicht, da nullglob
stattdessen die Shell-Option verwendet wird (in nicht verfügbar /bin/sh
).
Das Ausführen der Schleife über die Erweiterung des *b*
Musters anstelle der ls
Ausgaben 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 bash
Shell 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?
So drucken Sie die Dateinamen Folder
, die enthalten b
und nicht a
, in zsh
und 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 bash
Schale (die gerne zsh
auch kshs kopiert [[...]]
Operator) unter Verwendung von extglob
ksh88 glob Betreiber zu unterstützen und nullglob
die Wirkung von zsh der bekommen N
glob 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 cd
nicht funktionieren, wenn Sie Lesezugriff darauf haben, Folder
aber 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 case
Konstrukt 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