Shell: Verificando si una cadena contiene un carácter específico [duplicado]
#!/bin/sh
TMP=$(cd Folder && ls) for name in $TMP; do
if [[ "${name}" != *"a"* -a ${name} == *"b"* ]] ;then
echo $name
fi
done
Estaba tratando de generar los nombres que no tienen 'a' sino 'b' en su lugar. Este es el error que obtuve:
./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
Qué estoy haciendo mal aquí ?
Respuestas
Su principal problema es que está utilizando una coincidencia de patrones [[ ... ]]
en un script ejecutado por el /bin/sh
cual no admite este tipo de pruebas. Consulte también la pregunta titulada El script Shell arroja un error no encontrado cuando se ejecuta desde un archivo sh. Pero si se ingresan manualmente, los comandos funcionan
El otro problema es que está combinando la salida de ls
en una sola cadena antes de dividirla en palabras en espacios, tabuladores y nuevas líneas, y aplicar el nombre de archivo a las palabras generadas. Luego recorre el resultado de esto.
En lugar:
#!/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
Esto recorre todos los archivos en el Folder
directorio que tienen la letra b
en ellos y luego imprime los que no contienen un a
. La prueba para el a
personaje se realiza usando case ... esac
. El *a*
patrón coincide con un a
carácter en el nombre del archivo si lo hay, en cuyo caso no sucede nada. Las printf
sentencias están en el "caso predeterminado" que se ejecuta si no hay ninguna a
en la cadena.
Las pruebas con -e
y -L
están ahí para asegurarnos de que detectamos el caso de borde donde el patrón de bucle no coincide con nada. En ese caso, el patrón permanecerá sin expandir, por lo que para asegurarnos de que podemos distinguir entre un patrón sin expandir y un archivo existente real, probamos con -e
. La -L
prueba consiste en detectar el caso de borde adicional de un enlace simbólico que tiene el mismo nombre que el patrón de bucle, pero que no apunta a ningún lugar válido. El bash
siguiente código no necesita esto porque usa la nullglob
opción de shell en su lugar (no disponible en /bin/sh
).
Ejecutar el bucle sobre la expansión del *b*
patrón en lugar de cualquier ls
salida tiene la ventaja de que esto también puede tratar con nombres de archivo que contienen espacios, pestañas, líneas nuevas y caracteres globales de nombre de archivo (los nombres de archivo nunca se colocan en una sola cadena que luego intentamos iterar).
En el bash
shell, podría hacer lo mismo con una [[ ... ]]
prueba de coincidencia de patrones, como intentó hacer usted mismo:
#!/bin/bash
cd Folder || exit 1
shopt -s nullglob
for name in *b*; do
[[ $name != *a* ]] && printf '%s\n' "$name"
done
Ver también:
- ¿Por qué mi script de shell se ahoga con espacios en blanco u otros caracteres especiales?
- ¿Cuándo es necesaria la doble cotización?
- ¿Por qué * no * analizar `ls` (y qué hacer en su lugar)?
- ¿Por qué printf es mejor que echo?
Para imprimir los nombres de archivo Folder
que contienen b
y no a
, en zsh
y su operador glob ~
( excepto / y no ):
#! /bin/zsh -
set -o extendedglob
print -rC1 -- Folder/(*b*~*a*)(N:t)
(elimine el N
(para nullglob
, si prefiere que se informe un error donde no hay un archivo coincidente).
En ksh93
(el shell que introdujo ese [[...]]
operador (que no es parte de la sh
sintaxis estándar ) y sus operadores &
( y ) y !(...)
( no ):
#! /bin/ksh93 -
(
CDPATH= cd Folder &&
set -- ~(N)@(*b*&!(*a*)) &&
(($# > 0)) && printf '%s\n' "$@"
)
Con el bash
shell (que like zsh
también ha copiado el [[...]]
operador de ksh ), usando extglob
para admitir operadores glob ksh88 y nullglob
para obtener el efecto del N
calificador glob de zsh o ksh93
del ~(N)
operador glob y alguna doble negación con |
( o ) para lograr y :
#! /bin/bash -
(
shopt -s extglob nullglob
cd ./Folder &&
set -- !(!(*b*)|*a*) &&
(($# > 0)) && printf '%s\n' "$@"
)
En sh
sintaxis estándar :
#! /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' "$@"
)
Tenga en cuenta que las soluciones que utiliza cd
no funcionarán si tiene acceso de lectura Folder
pero no de búsqueda.
De manera más general, para responder a la pregunta general de cómo verificar si una cadena contiene otra sh
, lo mejor es con la case
construcción, que es cómo se hace la coincidencia de patrones sh
. Incluso podrías usar una función auxiliar:
contains()
case "$1" in
(*"$2"*) true;;
(*) false;;
esac
Para usar como si:
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