Tengo una cadena en el shell de Linux. Esta cadena contiene guiones bajos.

Quiero extraer una subcadena de la cadena.

Quiero extraer la subcadena después de la tercera aparición de un guión bajo, contado desde el final de la cadena.

file_name='email_Tracking_export_history_2018_08_15'
string_name="${file_name#*_*_*_}"
file_name2='email_Tracking_export_2018_08_15'
string_name2="${file_name2#*_*_*_}"

echo "$string_name"
echo "$string_name2"

El resultado

history_2018_08_15
2018_08_15

Como puede ver, string_name="${file_name#*_*_*_}" no funciona correctamente.

Resultado deseado:

2018_08_15
2018_08_15

¿Cómo puedo lograr el resultado deseado?

3
nmr 10 sep. 2018 a las 06:21

6 respuestas

La mejor respuesta

Puedes hacerlo en un solo paso, pero es un poco complicado. Después de configurar el nombre del archivo

file_name='email_Tracking_export_history_2018_08_15'

Obtenemos la subcadena que contiene todo excepto lo que queremos tener al final:

$ echo "${file_name%_*_*_*}"
email_Tracking_export_history

Esto es casi lo que queremos, solo falta un guión bajo, por lo que agregamos que:

$ echo "${file_name%_*_*_*}_"
email_Tracking_export_history_

Ahora sabemos lo que tenemos que eliminar desde el comienzo de la cadena e insertarlo en la expansión ${word#pattern}:

$ echo "${file_name#"${file_name%_*_*_*}_"}"
2018_08_15

O la asignamos a una variable para su uso posterior:

string_name=${file_name#"${file_name%_*_*_*}_"}
              └───┬───┘ │  └───┬───┘ └─┬──┘  │
             outer word │  inner word  └────────inner pattern
                        └───outer pattern────┘

Y análoga para la segunda cuerda.

2
Benjamin W. 10 sep. 2018 a las 17:20

¿expr ya está prohibido en el infierno más profundo incluso para la coincidencia de cadenas ?:

$ expr "$file_name" : '.*_\([^_]*_[^_]*_[^_]*\)'
2018_08_15
$ expr "$file_name2" : '.*_\([^_]*_[^_]*_[^_]*\)'
2018_08_15

De https://www.tldp.org/LDP/abs/html /string-manipulation.html:

expr "$string" : '.*\($substring\)'

    Extracts $substring at end of $string, where $substring is a regular expression.
0
James Brown 10 sep. 2018 a las 07:06

¿Qué hay de usar regex en bash:

#!/bin/bash

# Extract substring from string after 3rd occurrence in reverse
function extract() {
    if [[ "$1" =~ _([^_]+_[^_]+_[^_]+$) ]]; then
        echo "${BASH_REMATCH[1]}"
    fi
}

file_name='email_Tracking_export_history_2018_08_15'
string_name=$(extract $file_name)

file_name2='email_Tracking_export_2018_08_15'
string_name2=$(extract $file_name2)

echo "$string_name"
echo "$string_name2"
0
tshiono 10 sep. 2018 a las 04:01
% echo $file_name | rev | cut -f1-3 -d'_' | rev
2018_08_15
% echo $file_name2 | rev | cut -f1-3 -d'_' | rev
2018_08_15

rev invierte la cadena, lo que facilita el recuento de las 3 ocurrencias de guiones bajos. La parte de la cadena que desea extraer se invierte.

0
user1551605 10 sep. 2018 a las 05:56

Usando (la mayoría) sed y BRE:

sed 's/.*_\([^_]*\(_[^_]*\)\{2\}\)$/\1/' <<< "$file_name"
2018_08_15

Usando GNU sed y ERE:

sed -r 's/.*_([^_]*(_[^_]*){2})$/\1/' <<< "$file_name"
2018_08_15
0
oliv 10 sep. 2018 a las 06:07

Use una variable temporal:

file_name='email_Tracking_export_history_2018_08_15'
temp="${file_name%_*_*_*}"
string_name="${file_name/${temp}_}"
file_name2='email_Tracking_export_2018_08_15'
temp="${file_name2%_*_*_*}"
string_name2="${file_name2/${temp}_}"

echo "$string_name"
echo "$string_name2"
0
Ipor Sircer 10 sep. 2018 a las 03:38