2924 Stimmen

Wie splitte ich eine Zeichenkette an einem Begrenzer in der Bash?

Ich habe diese Zeichenfolge in einer Variablen gespeichert:

IN="bla@some.com;john@home.com"

Nun möchte ich die Zeichenketten aufteilen nach ; Begrenzungszeichen, so dass ich habe:

ADDR1="bla@some.com"
ADDR2="john@home.com"

Ich brauche nicht unbedingt die ADDR1 y ADDR2 Variablen. Wenn sie Elemente eines Arrays sind, ist das sogar noch besser.


Nach den Vorschlägen aus den nachstehenden Antworten kam ich zu folgendem Ergebnis, das dem entspricht, was ich wollte:

#!/usr/bin/env bash

IN="bla@some.com;john@home.com"

mails=$(echo $IN | tr ";" "\n")

for addr in $mails
do
    echo "> [$addr]"
done

Ausgabe:

> [bla@some.com]
> [john@home.com]

Es gab eine Lösung, die die Einstellung Interner_Feld_Trenner (IFS) an ; . Ich bin mir nicht sicher, was mit dieser Antwort passiert ist, wie setzt man IFS zurück zum Standard?

RE: IFS Lösung, ich habe das ausprobiert und es funktioniert, ich behalte die alte IFS und dann wiederherstellen:

IN="bla@some.com;john@home.com"

OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
    echo "> [$x]"
done

IFS=$OIFS

Übrigens, als ich versuchte

mails2=($IN)

Ich habe nur die erste Zeichenkette erhalten, wenn ich sie in einer Schleife ohne Klammern ausgedruckt habe $IN es funktioniert.

30 Stimmen

In Bezug auf Ihr "Edit2": Sie können einfach "IFS aufheben" und der Standardzustand wird wiederhergestellt. Es besteht keine Notwendigkeit, ihn explizit zu speichern und wiederherzustellen, es sei denn, Sie haben Grund zu der Annahme, dass er bereits auf einen anderen Wert als den Standardwert gesetzt wurde. Wenn Sie dies innerhalb einer Funktion tun (und wenn Sie es nicht tun, warum nicht?), können Sie IFS als lokale Variable setzen, die nach dem Verlassen der Funktion auf ihren vorherigen Wert zurückgesetzt wird.

33 Stimmen

@BrooksMoses: (a) +1 für die Verwendung local IFS=... wenn möglich; (b) -1 für unset IFS wird IFS nicht genau auf seinen Standardwert zurückgesetzt, obwohl ich glaube, dass sich ein nicht gesetzter IFS genauso verhält wie der Standardwert von IFS ($' \t\n '), aber es scheint eine schlechte Praxis zu sein, blind davon auszugehen, dass Ihr Code niemals mit einem benutzerdefinierten IFS-Wert aufgerufen werden wird; (c) eine andere Idee ist, eine Subshell aufzurufen: (IFS=$custom; ...) Wenn die Subshell beendet wird, kehrt IFS zu dem ursprünglichen Zustand zurück.

0 Stimmen

Ich möchte nur einen kurzen Blick auf die Pfade werfen, um zu entscheiden, wohin ich eine ausführbare Datei werfen soll, also habe ich auf die Ausführung von ruby -e "puts ENV.fetch('PATH').split(':')" . Wenn Sie reine Bash bleiben wollen, hilft es nicht, wenn Sie eine beliebige Skriptsprache die eine eingebaute Aufteilung hat, ist einfacher.

0voto

Ishaan Kapoor Punkte 34
ADDR1=${IN%%;*}
ADDR2=${IN##*;}

-1voto

Christoph Punkte 544

Editar: Es tut mir leid, ich hatte irgendwo auf SO gelesen, dass perl wird von POSIX gefordert, also dachte ich, es wäre legitim, es zu benutzen. Aber auf unix.stackexchange.com erklären mehrere Benutzer perl ist NICHT Teil der POSIX-Spezifikation.

Meine Lösung: Eine Funktion, die mit perl 's split um die Arbeit zu erledigen.

Mit ausführlichen Kommentaren:

#!/bin/bash

# This function is a wrapper for Perl's split.\
# \
# Since we cannot return an array like in Perl,
# it takes the name of the resulting array as last
# argument.\
# \
# See https://perldoc.perl.org/functions/split for usage info
# and examples.\
# \
# If you provide a Perl regexp that contains e. g. an escaped token like \b,
# space(s) and/or capture group(s), it must be quoted, and e. g. /\b/ must
# be single-quoted.\
# Thus, it's best to generally single-quote a Perl regexp.
function split # Args: <Element separator regexp> <string> <array name>
{
    (($# != 3)) && echo "${FUNCNAME[0]}: Wrong number of arguments, returning." && return 1

    local elementSepRE=$1
    local string=$2
    local -n array=$3

    local element i=0

    # Attention! read does Word Splitting on each line!
    # I must admit I didn't know that so far.
    # This removes leading and trailing spaces, exactly
    # what we don't want.
    # Thus, we set IFS locally to newline only.
    local IFS=$'\n'

    while read element; do
        # As opposed to array+=($element),
        # this preserves leading and trailing spaces.
        array[i++]=$element
    done <<<$(_perl_split)
}

# This function calls Perl's split function and prints the elements of the
# resulting array on separate lines.\
# It uses the caller's $elementSepRE and $string.
function _perl_split
{
    # A heredoc is a great way of embedding a Perl script.
    # N.B.: - Shell variables get expanded.
    #         - Thus:
    #           - They must be quoted.
    #           - Perl scalar variables must be escaped.
    #       - The backslash of \n must be escaped to protect it.
    #       - Instead of redirecting a single heredoc to perl, we may
    #         use multiple heredocs with cat within a command group and
    #         pipe the result to perl.
    #         This enables us to conditionally add certain lines of code.

    {
        cat <<-END
            my \$elementSepRE=q($elementSepRE);
        END

        # If $elementSepRE is a literal Perl regexp, qr must be applied
        # to it in order to use it.
        # N.B.: We cannot write this condition in Perl because when perl
        # compiles the script, all statements are checked for validity,
        # no matter if they will actually be executed or not.
        # And if $elementSepRE was e. g. == ', the line below – although
        # not to be executed – would give an error because of an unterminated
        # single-quoted string.
        [[ $elementSepRE =~ ^m?/ && $elementSepRE =~ /[msixpodualn]*$ ]] && cat <<-END
            \$elementSepRE=qr$elementSepRE;
        END

        cat <<-END
            my @array=split(\$elementSepRE, q($string));

            print(\$_ . "\\n") for (@array);
        END
    } | perl
}

Und das Gleiche ohne Kommentare für diejenigen, die auf einen Blick sehen, was vor sich geht ;)

#!/bin/bash

# This function is a wrapper for Perl's split.\
# \
# Since we cannot return an array like in Perl,
# it takes the name of the resulting array as last
# argument.\
# \
# See https://perldoc.perl.org/functions/split for usage info
# and examples.\
# \
# If you provide a Perl regexp that contains e. g. an escaped token like \b,
# space(s) and/or capture group(s), it must be quoted, and e. g. /\b/ must
# be single-quoted.\
# Thus, it's best to generally single-quote a Perl regexp.
function split # Args: <Element separator regexp> <string> <array name>
{
    (($# != 3)) && echo "${FUNCNAME[0]}: Wrong number of arguments, returning." && return 1

    local elementSepRE=$1
    local string=$2
    local -n array=$3

    local element i=0

    local IFS=$'\n'

    while read element; do
        array[i++]=$element
    done <<<$(_perl_split)
}

function _perl_split
{
    {
        cat <<-END
            my \$elementSepRE=q($elementSepRE);
        END

        [[ $elementSepRE =~ ^m?/ && $elementSepRE =~ /[msixpodualn]*$ ]] && cat <<-END
            \$elementSepRE=qr$elementSepRE;
        END

        cat <<-END
            my @array=split(\$elementSepRE, q($string));

            print(\$_ . "\\n") for (@array);
        END
    } | perl
}

-8voto

Es gibt zwei einfache Methoden:

cat "text1;text2;text3" | tr " " "\n"

y

cat "text1;text2;text3" | sed -e 's/ /\n/g'

3 Stimmen

Ich glaube, Sie haben cat y echo verwirrt. cat liest aus Dateien. echo liest den angegebenen Text.

-11voto

Fil Punkte 31

Noch eine späte Antwort... Wenn Sie Java-affin sind, hier ist die bashj ( https://sourceforge.net/projects/bashj/ ) Lösung:

#!/usr/bin/bashj

#!java

private static String[] cuts;
private static int cnt=0;
public static void split(String words,String regexp) {cuts=words.split(regexp);}
public static String next() {return(cnt<cuts.length ? cuts[cnt++] : "null");}

#!bash

IN="bla@some.com;john@home.com"

: j.split($IN,";")    # java method call

while true
do
    NAME=j.next()     # java method call
    if [ $NAME != null ] ; then echo $NAME ; else exit ; fi
done

0 Stimmen

Man kann nicht von jemandem erwarten, dass er sein Äußeres ändert, nur damit er eine einzige Aufgabe erfüllen kann.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X