Odpowiedzi:
Użyj cut
z _
jako separatora pól i uzyskaj żądane pola:
A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"
Możesz także użyć echo
i potoku zamiast ciągu Tutaj:
A="$(echo 'one_two_three_four_five' | cut -d'_' -f2)"
B="$(echo 'one_two_three_four_five' | cut -d'_' -f4)"
Przykład:
$ s='one_two_three_four_five'
$ A="$(cut -d'_' -f2 <<<"$s")"
$ echo "$A"
two
$ B="$(cut -d'_' -f4 <<<"$s")"
$ echo "$B"
four
$ echo $FILE
my_user/my_folder/file.csv
$ A="$(cut -d'/' -f2 <<<"$FILE")"
$ echo $A
[file]*
Czy wiesz, co się tutaj dzieje?
echo "${s##*_}"
Używając tylko konstruktorów sh POSIX, możesz używać konstrukcji podstawiania parametrów do parsowania jednego separatora na raz. Zauważ, że ten kod zakłada, że istnieje wymagana liczba pól, w przeciwnym razie ostatnie pole zostanie powtórzone.
string='one_two_three_four_five'
remainder="$string"
first="${remainder%%_*}"; remainder="${remainder#*_}"
second="${remainder%%_*}"; remainder="${remainder#*_}"
third="${remainder%%_*}"; remainder="${remainder#*_}"
fourth="${remainder%%_*}"; remainder="${remainder#*_}"
Alternatywnie, możesz użyć niecytowanego podstawienia parametru z wyłączonym rozszerzaniem symboli wieloznacznych i IFS
ustawionym na znak separatora (działa to tylko wtedy, gdy separator jest pojedynczym znakiem spacji lub dowolna sekwencja białych znaków jest separatorem).
string='one_two_three_four_five'
set -f; IFS='_'
set -- $string
second=$2; fourth=$4
set +f; unset IFS
Spowoduje to zablokowanie parametrów pozycji. Jeśli zrobisz to w funkcji, wpływa to tylko na parametry pozycyjne funkcji.
Jeszcze innym podejściem jest użycie read
wbudowanego.
IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF
unset IFS
nie powraca IFS
do wartości domyślnych. Jeśli później ktoś to zrobi, OldIFS="$IFS"
będzie miał wartość zerową w OldIFS. Zakłada się również, że poprzednia wartość IFS jest wartością domyślną, co jest bardzo możliwe (i przydatne), że nie będzie. Jedynym prawidłowym rozwiązaniem jest przechowywanie, old="$IFS"
a później przywracanie z IFS = „$ old”. Lub ... użyj podpowłoki (...)
. Albo jeszcze lepiej przeczytaj moją odpowiedź.
unset IFS
nie przywraca IFS
wartości domyślnej, ale przywraca podział pola do domyślnego efektu. Tak, jest to ograniczenie, ale w praktyce jest zwykle dopuszczalne. Problem z podpowłoką polega na tym, że musimy wyciągnąć z niej dane. Pokazuję rozwiązanie, które nie zmienia stanu na końcu read
. (Działa w powłokach POSIX, ale IIRC nie w powłoce Bourne'a, ponieważ działałby read
w podpowłoce z powodu dokumentu tutaj.) Użycie <<<
odpowiedzi w tobie jest wariantem, który działa tylko w ksh / bash / zsh.
user/my_folder/[this_is_my_file]*
? Po tych krokach otrzymuję[this_is_my_file]*
/
.
Chciałem zobaczyć awk
odpowiedź, więc oto jedna:
A=$(awk -F_ '{print $2}' <<< 'one_two_three_four_five')
B=$(awk -F_ '{print $4}' <<< 'one_two_three_four_five')
awk -F_ '{print $NF}' <<< 'one_two_3_4_five'
Najprostszym sposobem (dla muszli z <<<) jest:
IFS='_' read -r a second a fourth a <<<"$string"
Używanie zmiennej czasowej $a
zamiast $_
ponieważ jedna powłoka narzeka.
W pełnym skrypcie:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<<"$string"
echo "$second $fourth"
Bez zmian IFS, nie ma problemów z set -f
(rozwinięcie nazwy ścieżki) Brak zmian parametrów pozycyjnych („$ @”).
W przypadku rozwiązania przenośnego dla wszystkich powłok (tak, wszystkie POSIX włącznie) bez zmiany IFS lub set -f
użyj (nieco bardziej złożonego) odpowiednika heredoc:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<-_EOF_
$string
_EOF_
echo "$second $fourth"
Zrozum, że te rozwiązania (zarówno tutaj-doc, jak i użycie <<<
usunie wszystkie końcowe znaki nowej linii.
I że jest to zaprojektowane dla zmiennej zawartości „jednej linii”.
Rozwiązania dla wielu linii są możliwe, ale wymagają bardziej złożonych konstrukcji.
Bardzo proste rozwiązanie jest możliwe w wersji bash 4.4
readarray -d _ -t arr <<<"$string"
echo "array ${arr[1]} ${arr[3]}" # array numbers are zero based.
Nie ma odpowiednika dla powłok POSIX, ponieważ wiele powłok POSIX nie ma tablic.
W przypadku powłok posiadających tablice mogą być tak proste, jak:
(testowane w attsh, lksh, mksh, ksh i bash)
set -f; IFS=_; arr=($string)
Ale z dużą ilością dodatkowej instalacji hydraulicznej, aby zachować i zresetować zmienne i opcje:
string='one_* *_three_four_five'
case $- in
*f*) noglobset=true; ;;
*) noglobset=false;;
esac
oldIFS="$IFS"
set -f; IFS=_; arr=($string)
if $noglobset; then set -f; else set +f; fi
echo "two=${arr[1]} four=${arr[3]}"
W Zsh tablice zaczynają się od 1 i domyślnie nie dzielą łańcucha.
Tak więc należy wprowadzić pewne zmiany, aby działało to w Zsh.
read
są proste, o ile OP nie chce wyodrębnić 76. i 127. elementu z długiego łańcucha ...
readarray
w tej sytuacji może być łatwiejszy w użyciu.
Dzięki zsh
możesz podzielić ciąg (on _
) na tablicę:
elements=(${(s:_:)string})
a następnie uzyskać dostęp do każdego elementu za pomocą indeksu tablicy:
print -r ${elements[4]}
Należy pamiętać, że w zsh
(w przeciwieństwie do ksh
/ bash
) indeksy tablicowe zaczynają się od 1 .
set -f
ostrzeżenie do pierwszego rozwiązania. ... *
może gwiazdki ?
set -f
? Nie używam read
/ IFS
. Wypróbuj moje rozwiązania z ciągiem podobnym *_*_*
lub czymkolwiek ...
Kolejny przykład awk; prostsze do zrozumienia.
A=\`echo one_two_three_four_five | awk -F_ '{print $1}'\`
B=\`echo one_two_three_four_five | awk -F_ '{print $2}'\`
C=\`echo one_two_three_four_five | awk -F_ '{print $3}'\`
... and so on...
Może być również używany ze zmiennymi.
Załóżmy, że:
this_str = "one_two_three_four_five"
Następnie działają następujące elementy:
A = `echo $ {this_str} | awk -F_ '{print $ 1}' '
B = `echo $ {this_str} | awk -F_ '{print $ 2}' '
C = `echo $ {this_str} | awk -F_ '{print $ 3}' '
... i tak dalej ...