Powłoka: sprawdzanie, czy ciąg zawiera określony znak [duplikat]
#!/bin/sh
TMP=$(cd Folder && ls) for name in $TMP; do
if [[ "${name}" != *"a"* -a ${name} == *"b"* ]] ;then
echo $name
fi
done
Próbowałem wypisać nazwy, w których nie ma „a”, ale zamiast tego „b”. Oto błąd, który otrzymałem:
./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
Co ja tu robię źle?
Odpowiedzi
Twoim głównym problemem jest to, że używasz dopasowania do wzorca w [[ ... ]]
wykonywanym skrypcie, /bin/sh
który nie obsługuje tego typu testów. Zobacz także pytanie zatytułowane Skrypt powłoki zgłasza błąd „not found” po uruchomieniu z pliku sh. Ale jeśli zostaną wprowadzone ręcznie, polecenia działają
Inną kwestią jest to, że łączysz dane wyjściowe programu ls
w jeden ciąg przed podzieleniem go na słowa ze spacjami, tabulatorami i znakami nowej linii oraz zastosowaniem globalizacji nazw plików do wygenerowanych słów. Następnie zapętlasz wynik tego.
Zamiast:
#!/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
To zapętla wszystkie pliki w Folder
katalogu, które zawierają literę b
, a następnie wypisuje te, które nie zawierają a
. Test a
postaci wykonuje się za pomocą case ... esac
. *a*
Wzór pasuje do a
znaku w nazwie pliku, jeśli jest tylko jedna, w tym przypadku nic się nie dzieje. Te printf
oświadczenia jest w przypadku „domyślnym”, który zostanie wykonany, jeśli nie jest a
w struny.
Testy z -e
i -L
są tam, aby upewnić się, że złapiemy sprawę krawędź, gdzie wzór pętla niczego nie pasujące. W takim przypadku wzorzec pozostanie nierozwinięty, więc aby upewnić się, że możemy odróżnić nierozwinięty wzorzec od istniejącego pliku, testujemy za pomocą -e
. -L
Test jest złapać kolejny przypadek krawędzi dowiązania symbolicznego, który ma taką samą nazwę jak wzór pętli, ale to nie wskazuje na dowolnym ważny. Poniższy bash
kod nie potrzebuje tego, ponieważ nullglob
zamiast tego używa opcji powłoki (niedostępna w /bin/sh
).
Uruchamianie pętli nad rozwinięciem *b*
wzorca zamiast jakichkolwiek danych ls
wyjściowych ma tę zaletę, że jest w stanie poradzić sobie również z nazwami plików zawierającymi spacje, tabulatory, znaki nowej linii i znaki globbingu (nazwy plików nigdy nie są umieszczane w jednym ciągu iteruj).
W bash
powłoce możesz zrobić to samo z [[ ... ]]
testem dopasowania wzorców, tak jak próbowałeś zrobić sam:
#!/bin/bash
cd Folder || exit 1
shopt -s nullglob
for name in *b*; do
[[ $name != *a* ]] && printf '%s\n' "$name"
done
Zobacz też:
- Dlaczego mój skrypt powłoki dławi się białymi znakami lub innymi znakami specjalnymi?
- Kiedy konieczne jest podwójne cytowanie?
- Dlaczego * nie * przeanalizować `ls` (i co zrobić zamiast tego)?
- Dlaczego printf jest lepszy niż echo?
Aby wydrukować nazwy plików, Folder
które zawierają b
i nie a
, operator in zsh
i ~
( z wyjątkiem / i nie ) glob:
#! /bin/zsh -
set -o extendedglob
print -rC1 -- Folder/(*b*~*a*)(N:t)
(usuń N
(dla nullglob
, jeśli wolisz zgłaszać błąd w przypadku braku pasującego pliku).
W ksh93
(powłoce, która wprowadziła ten [[...]]
operator (który nie jest częścią standardowej sh
składni) oraz jego &
( i ) i !(...)
( nie ) operatory:
#! /bin/ksh93 -
(
CDPATH= cd Folder &&
set -- ~(N)@(*b*&!(*a*)) &&
(($# > 0)) && printf '%s\n' "$@"
)
Dzięki bash
powłoce (który podobnie jak zsh
również kopiowane KSH za [[...]]
operatora), używając extglob
do wspierania ksh88 operatorów glob i nullglob
aby uzyskać efekt zsh za N
glob kwalifikacjach lub ksh93
„s ~(N)
operatora glob i jakąś podwójną negację z |
( lub ), aby osiągnąć i :
#! /bin/bash -
(
shopt -s extglob nullglob
cd ./Folder &&
set -- !(!(*b*)|*a*) &&
(($# > 0)) && printf '%s\n' "$@"
)
W standardowej sh
składni:
#! /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' "$@"
)
Zwróć uwagę, że rozwiązania, które używają cd
, nie będą działać, jeśli masz dostęp do odczytu, Folder
ale nie masz dostępu do wyszukiwania.
Mówiąc bardziej ogólnie, aby odpowiedzieć na ogólne pytanie, jak sprawdzić, czy ciąg zawiera inny ciąg w sh
, najlepiej jest skorzystać z case
konstrukcji, która polega na dopasowaniu wzorca sh
. Możesz nawet użyć funkcji pomocniczej:
contains()
case "$1" in
(*"$2"*) true;;
(*) false;;
esac
Aby używać tak, jakby:
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