string - separar - Cómo verificar si una cadena contiene una subcadena en Bash
manipulacion de cadenas de texto bash (21)
Tengo una cadena en Bash:
string="My string"
¿Cómo puedo probar si contiene otra cadena?
if [ $string ?? ''foo'' ]; then
echo "It''s there!"
fi
Donde ??
es mi operador desconocido ¿Uso echo y grep
?
if echo "$string" | grep ''foo''; then
echo "It''s there!"
fi
Eso se ve un poco torpe.
Respuesta compatible
Como ya hay muchas respuestas utilizando las funciones específicas de Bash, hay una forma de trabajar bajo shells con características más pobres, como busybox :
[ -z "${string##*$reqsubstr*}" ]
En la práctica, esto podría dar:
string=''echo "My string"''
for reqsubstr in ''o "M'' ''alt'' ''str'';do
if [ -z "${string##*$reqsubstr*}" ] ;then
echo "String ''$string'' contain substring: ''$reqsubstr''."
else
echo "String ''$string'' don''t contain substring: ''$reqsubstr''."
fi
done
Esto se probó bajo bash , dash , ksh y ash (busybox), y el resultado es siempre:
String ''echo "My string"'' contain substring: ''o "M''.
String ''echo "My string"'' don''t contain substring: ''alt''.
String ''echo "My string"'' contain substring: ''str''.
En una funcion
Según lo solicitado por @EeroAaltonen, aquí hay una versión de la misma demostración, probada bajo los mismos shells:
myfunc() {
reqsubstr="$1"
shift
string="$@"
if [ -z "${string##*$reqsubstr*}" ] ;then
echo "String ''$string'' contain substring: ''$reqsubstr''.";
else
echo "String ''$string'' don''t contain substring: ''$reqsubstr''."
fi
}
Entonces:
$ myfunc ''o "M'' ''echo "My String"''
String ''echo "My String"'' contain substring ''o "M''.
$ myfunc ''alt'' ''echo "My String"''
String ''echo "My String"'' don''t contain substring ''alt''.
Aviso: usted tiene que escapar o doble comillas y / o comillas dobles:
$ myfunc ''o "M'' echo "My String"
String ''echo My String'' don''t contain substring: ''o "M''.
$ myfunc ''o "M'' echo /"My String/"
String ''echo "My String"'' contain substring: ''o "M''.
Función simple
Esto fue probado bajo busybox , dash y, por supuesto, bash :
stringContain() { [ -z "${2##*$1*}" ]; }
¡Eso es todo amigos!
Entonces ahora:
$ if stringContain ''o "M3'' ''echo "My String"'';then echo yes;else echo no;fi
no
$ if stringContain ''o "M'' ''echo "My String"'';then echo yes;else echo no;fi
yes
... O si la cadena enviada podría estar vacía, como señaló @Sjlver, la función se convertiría en:
stringContain() { [ -z "${2##*$1*}" ] && [ -z "$1" -o -n "$2" ]; }
o según lo sugerido por el comentario de Adrian Günter , evitando -o
switche:
stringContain() { [ -z "${2##*$1*}" ] && { [ -z "$1" ] || [ -n "$2" ] ;} ; }
Con cuerdas vacías:
$ if stringContain '''' ''''; then echo yes; else echo no; fi
yes
$ if stringContain ''o "M'' ''''; then echo yes; else echo no; fi
no
Bash4 + ejemplos. Nota: no usar comillas causará problemas cuando las palabras contengan espacios, etc. Siempre cite en bash IMO.
Aquí hay algunos ejemplos de BASH4 +:
Ejemplo 1, verifique ''sí'' en la cadena (no distingue mayúsculas y minúsculas):
if [[ "${str,,}" == *"yes"* ]] ;then
Ejemplo 2, verifique ''sí'' en la cadena (no distingue mayúsculas y minúsculas):
if [[ "$(echo "$str" | tr ''[:upper:]'' ''[:lower:]'')" == *"yes"* ]] ;then
Ejemplo 3, verifique ''sí'' en la cadena (distingue entre mayúsculas y minúsculas):
if [[ "${str}" == *"yes"* ]] ;then
Ejemplo 4, verifique ''sí'' en la cadena (distingue entre mayúsculas y minúsculas):
if [[ "${str}" =~ "yes" ]] ;then
Ejemplo 5, coincidencia exacta (distingue mayúsculas y minúsculas):
if [[ "${str}" == "yes" ]] ;then
Ejemplo 6, coincidencia exacta (no distingue mayúsculas y minúsculas):
if [[ "${str,,}" == "yes" ]] ;then
Ejemplo 7, coincidencia exacta:
if [ "$a" = "$b" ] ;then
disfrutar.
Coincidencia exacta de la palabra:
string=''My long string''
exactSearch=''long''
if grep -E -q "/b${exactSearch}/b" <<<${string} >/dev/null 2>&1
then
echo "It''s there"
fi
Como Pablo mencionó en su comparación de desempeño:
if echo "abcdefg" | grep -q "bcdef"; then
echo "String contains is true."
else
echo "String contains is not true."
fi
Esto es compatible con POSIX como la respuesta ''case'' $ string ''provista por Marcus, pero es un poco más fácil de leer que la respuesta de la declaración del caso. También tenga en cuenta que esto será mucho más lento que usar una declaración de caso, como lo señaló Paul, no lo use en un bucle.
Debe recordar que los scripts de shell son menos de un lenguaje y más de una colección de comandos. Instintivamente, usted piensa que este "lenguaje" requiere que siga un if
con un [
o un [[
. Ambos son solo comandos que devuelven un estado de salida que indica el éxito o el fracaso (como cualquier otro comando). Por esa razón usaría grep
, y no el [
comando.
Solo haz:
if grep -q foo <<<"$string"; then
echo "It''s there"
fi
Ahora que está pensando if
probando el estado de salida del comando que lo sigue (complete con punto y coma). ¿Por qué no reconsiderar el origen de la cadena que está probando?
## Instead of this
filetype="$(file -b "$1")"
if grep -q "tar archive" <<<"$filetype"; then
#...
## Simply do this
if file -b "$1" | grep -q "tar archive"; then
#...
La opción -q
hace que grep no genere nada, ya que solo queremos el código de retorno. <<<
hace que el shell expanda la siguiente palabra y la use como entrada para el comando, una versión de una línea del documento <<
aquí (no estoy seguro si esto es estándar o un bashismo).
Descubrí que necesitaba esta funcionalidad con bastante frecuencia, así que estoy usando una función de shell hecha en casa en mi .bashrc
como esta, que me permite reutilizarla con la frecuencia que necesito, con un nombre fácil de recordar:
function stringinstring()
{
case "$2" in
*"$1"*)
return 0
;;
esac
return 1
}
Para comprobar si $string1
(por ejemplo, abc ) está contenido en $string2
(por ejemplo, 123abcABC ), solo necesito ejecutar stringinstring "$string1" "$string2"
y verificar el valor de retorno, por ejemplo
stringinstring "$str1" "$str2" && echo YES || echo NO
Entonces, hay muchas soluciones útiles para la pregunta, pero ¿cuál es la más rápida / usa el menor recurso?
Pruebas repetidas utilizando este marco:
/usr/bin/time bash -c ''a=two;b=onetwothree; x=100000; while [ $x -gt 0 ]; do TEST ; x=$(($x-1)); done''
Reemplazo de PRUEBA cada vez:
[[ $b =~ $a ]] 2.92user 0.06system 0:02.99elapsed 99%CPU
[ "${b/$a//}" = "$b" ] 3.16user 0.07system 0:03.25elapsed 99%CPU
[[ $b == *$a* ]] 1.85user 0.04system 0:01.90elapsed 99%CPU
case $b in *$a):;;esac 1.80user 0.02system 0:01.83elapsed 99%CPU
doContain $a $b 4.27user 0.11system 0:04.41elapsed 99%CPU
(DoContain estaba en la respuesta de F. Houri)
Y para las risitas:
echo $b|grep -q $a 12.68user 30.86system 3:42.40elapsed 19%CPU !ouch!
Por lo tanto, la opción de sustitución simple gana de manera predecible ya sea en una prueba extendida o en un caso. El estuche es portátil.
¡Llegar a 100000 greps es predeciblemente doloroso! La vieja regla sobre el uso de utilidades externas sin necesidad es cierta.
Esto también funciona:
if printf -- ''%s'' "$haystack" | egrep -q -- "$needle"
then
printf "Found needle in haystack"
fi
Y la prueba negativa es:
if ! printf -- ''%s'' "$haystack" | egrep -q -- "$needle"
then
echo "Did not find needle in haystack"
fi
Supongo que este estilo es un poco más clásico, menos dependiente de las características de Bash shell.
El argumento es pura paranoia de POSIX, usado para protegerse contra cadenas de entrada similares a opciones, tales como --abc
o -a
.
Nota: en un circuito cerrado, este código será mucho más lento que el uso de las funciones internas de Bash shell, ya que uno (o dos) procesos separados se crearán y conectarán a través de tuberías.
Intente oobash es una biblioteca de cadenas de estilo OO para bash 4. Tiene soporte para diéresis alemanas. Está escrito en bash. Muchas funciones están disponibles: -base64Decode
, -base64Encode
, -capitalize
, -center
, -charAt
, -concat
, -contains
, -count
, -endsWith
, -equals
, -equalsIgnoreCase
, -reverse
, -hashCode
, -indexOf
, -isAlnum
-isAlpha
, -isAscii
, -isDigit
, -isEmpty
, -isHexDigit
-isLowerCase
, -isSpace
, -isPrintable
, -isUpperCase
, -isVisible
, -lastIndexOf
, -matches
, -replaceAll
-- -replaceFirst
-startsWith
-substring
-startsWith
-substring
-startsWith
-substring
-startsWith
-substring
-startsWith
-substring
, -swapCase
, -toLowerCase
, -toString
, -toUpperCase
, -trim
, y -zfill
.
Mira el ejemplo que contiene:
[Desktop]$ String a testXccc
[Desktop]$ a.contains tX
true
[Desktop]$ a.contains XtX
false
La respuesta aceptada es la mejor, pero como hay más de una forma de hacerlo, aquí hay otra solución:
if [ "$string" != "${string/foo/}" ]; then
echo "It''s there!"
fi
${var/search/replace}
es $var
con la primera instancia de search
reemplazada por replace
, si se encuentra (no cambia $var
). Si intentas reemplazar foo
por nada y la cadena ha cambiado, obviamente se encontró foo
.
Me gusta sed
substr="foo"
nonsub="$(echo "$string" | sed "s/$substr//")"
hassub=0 ; [ "$string" != "$nonsub" ] && hassub=1
Edición, Lógica:
Utilice sed para eliminar la instancia de subcadena de cadena
Si la cadena nueva difiere de la cadena antigua, existe la subcadena
Mi .bash_profile y cómo utilicé grep si la RUTA incluía mis direcciones de 2 contenedores, no los agregue
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
U=~/.local.bin:~/bin
if ! echo "$PATH" | grep -q "home"; then
export PATH=$PATH:${U}
fi
No estoy seguro de usar una declaración if, pero puede obtener un efecto similar con una declaración de caso:
case "$string" in
*foo*)
# Do stuff
;;
esac
Qué tal esto:
text=" <tag>bmnmn</tag> "
if [[ "$text" =~ "<tag>" ]]; then
echo "matched"
else
echo "not matched"
fi
Si prefiere el enfoque de expresiones regulares:
string=''My string'';
if [[ $string =~ .*My.* ]]
then
echo "It''s there!"
fi
También puede usar la respuesta de Marcus (* comodines) fuera de una declaración de caso, si usa corchetes dobles:
string=''My long string''
if [[ $string == *"My long"* ]]; then
echo "It''s there!"
fi
Tenga en cuenta que los espacios en la cadena de la aguja deben colocarse entre comillas dobles, y los comodines *
deben estar fuera.
Uno es:
[ $(expr $mystring : ".*${search}.*") -ne 0 ] && echo ''yes'' || echo ''no''
Yo uso esta función (una dependencia no incluida pero obvia). Pasa las pruebas que se muestran a continuación. Si la función devuelve un valor> 0, se encontró la cadena. Podrías devolver 1 o 0 con la misma facilidad.
function str_instr {
# Return position of ```str``` within ```string```.
# >>> str_instr "str" "string"
# str: String to search for.
# string: String to search.
typeset str string x
# Behavior here is not the same in bash vs ksh unless we escape special characters.
str="$(str_escape_special_characters "${1}")"
string="${2}"
x="${string%%$str*}"
if [[ "${x}" != "${string}" ]]; then
echo "${#x} + 1" | bc -l
else
echo 0
fi
}
function test_str_instr {
str_instr "(" "''foo@host (dev,web)''" | assert_eq 11
str_instr ")" "''foo@host (dev,web)''" | assert_eq 19
str_instr "[" "''foo@host [dev,web]''" | assert_eq 11
str_instr "]" "''foo@host [dev,web]''" | assert_eq 19
str_instr "a" "abc" | assert_eq 1
str_instr "z" "abc" | assert_eq 0
str_instr "Eggs" "Green Eggs And Ham" | assert_eq 7
str_instr "a" "" | assert_eq 0
str_instr "" "" | assert_eq 0
str_instr " " "Green Eggs" | assert_eq 6
str_instr " " " Green " | assert_eq 1
}
grep -q
es útil para este propósito.
Lo mismo usando awk
:
string="unix-bash 2389"
character="@"
printf ''%s'' "$string" | awk -vc="$character" ''{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }''
Salida:
Extraviado
string="unix-bash 2389"
character="-"
printf ''%s'' "$string" | awk -vc="$character" ''{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }''
Salida:
Encontró
Fuente original: http://unstableme.blogspot.com/2008/06/bash-search-letter-in-string-awk.html
Esta respuesta de desbordamiento de pila fue la única que atrapó el espacio y los caracteres del tablero:
# For null cmd arguments checking
to_check='' -t''
space_n_dash_chars='' -''
[[ $to_check == *"$space_n_dash_chars"* ]] && echo found
[[ $string == *foo* ]] && echo "It''s there" || echo "Couldn''t find"