Risposte:
Utilizzare cut
con _
come delimitatore di campo e ottenere i campi desiderati:
A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"
Puoi anche usare echo
e pipe invece della stringa Here:
A="$(echo 'one_two_three_four_five' | cut -d'_' -f2)"
B="$(echo 'one_two_three_four_five' | cut -d'_' -f4)"
Esempio:
$ 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]*
Sai cosa sta succedendo qui?
echo "${s##*_}"
Utilizzando solo costrutti sh POSIX, è possibile utilizzare costrutti di sostituzione dei parametri per analizzare un delimitatore alla volta. Si noti che questo codice presuppone che vi sia il numero richiesto di campi, altrimenti l'ultimo campo viene ripetuto.
string='one_two_three_four_five'
remainder="$string"
first="${remainder%%_*}"; remainder="${remainder#*_}"
second="${remainder%%_*}"; remainder="${remainder#*_}"
third="${remainder%%_*}"; remainder="${remainder#*_}"
fourth="${remainder%%_*}"; remainder="${remainder#*_}"
In alternativa, è possibile utilizzare una sostituzione dei parametri non quotata con l' espansione jolly disabilitata e IFS
impostata sul carattere delimitatore (funziona solo se il delimitatore è un singolo carattere non di spazi bianchi o se una sequenza di spazi vuoti è un delimitatore).
string='one_two_three_four_five'
set -f; IFS='_'
set -- $string
second=$2; fourth=$4
set +f; unset IFS
Questo blocca i parametri posizionali. Se lo fai in una funzione, sono interessati solo i parametri posizionali della funzione.
Ancora un altro approccio è usare il read
builtin.
IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF
unset IFS
non torna IFS
ai valori predefiniti. Se dopo quello qualcuno OldIFS="$IFS"
avrà un valore nullo all'interno di OldIFS. Inoltre, si presume che il valore precedente di IFS sia il valore predefinito, che è molto possibile (e utile) non essere. L'unica soluzione corretta è quella di archiviare old="$IFS"
e ripristinare successivamente con IFS = "$ old". Oppure ... usa una sotto-shell (...)
. O, meglio ancora, leggi la mia risposta.
unset IFS
non ripristina IFS
il valore predefinito, ma restituisce la divisione del campo con l'effetto predefinito. Sì, è una limitazione, ma di solito accettabile in pratica. Il problema con una subshell è che dobbiamo estrarne i dati. Mostro una soluzione che non cambia lo stato alla fine, con read
. (Funziona nelle shell POSIX, ma IIRC non nella shell Bourne perché eseguirà la read
in una shell secondaria a causa del documento qui.) Usare <<<
come nella risposta è una variante che funziona solo in ksh / bash / zsh.
user/my_folder/[this_is_my_file]*
? Quello che ottengo quando seguo questi passaggi è[this_is_my_file]*
/
.
Volevo vedere una awk
risposta, quindi eccone una:
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'
Il modo più semplice (per shell con <<<) è:
IFS='_' read -r a second a fourth a <<<"$string"
Usare una variabile temporale $a
invece che $_
perché una shell si lamenta.
In uno script completo:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<<"$string"
echo "$second $fourth"
Nessuna modifica IFS, non problemi con set -f
(espansione del nome percorso) Nessuna modifica ai parametri posizionali ("$ @").
Per una soluzione portatile per tutte le shell (sì, inclusi tutti i POSIX) senza cambiare IFS o set -f
, utilizzare l'equivalente ereditario (un po 'più complesso):
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<-_EOF_
$string
_EOF_
echo "$second $fourth"
Comprendi che queste soluzioni (sia il documento qui sia l'uso di <<<
rimuoveranno tutte le nuove righe finali.
E che questo è progettato per un contenuto variabile "one liner". Le
soluzioni per multi-liners sono possibili ma richiedono costrutti più complessi.
Una soluzione molto semplice è possibile nella versione 4.4 di bash
readarray -d _ -t arr <<<"$string"
echo "array ${arr[1]} ${arr[3]}" # array numbers are zero based.
Non esiste un equivalente per le shell POSIX, poiché molte shell POSIX non hanno array.
Per le shell che hanno array può essere semplice come:
(testato lavorando in attsh, lksh, mksh, ksh e bash)
set -f; IFS=_; arr=($string)
Ma con un sacco di idraulica aggiuntiva per mantenere e ripristinare variabili e opzioni:
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]}"
In zsh, le matrici iniziano in 1 e non dividono la stringa per impostazione predefinita.
Quindi alcune modifiche devono essere fatte per farlo funzionare in zsh.
read
sono semplici fintanto che OP non vuole estrarre il 76 ° e il 127 ° elemento da una lunga stringa ...
readarray
potrebbe essere più facile da usare per quella situazione.
Con zsh
te puoi dividere la stringa (on _
) in un array:
elements=(${(s:_:)string})
e quindi accedere a ciascun / qualsiasi elemento tramite l'indice dell'array:
print -r ${elements[4]}
Tieni presente che in zsh
(diversamente da ksh
/ bash
) gli indici di array iniziano da 1 .
set -f
avviso alla prima soluzione. ... asterischi *
forse?
set -f
? Non sto usando read
/ IFS
. Prova le mie soluzioni con una stringa simile *_*_*
o qualsiasi altra cosa ...
Un altro esempio imbarazzante; più semplice da capire.
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...
Può essere utilizzato anche con variabili.
Supponiamo:
this_str = "one_two_three_four_five"
Quindi le seguenti operazioni:
A = `echo $ {this_str} | awk -F_ '{print $ 1}' `
B =` echo $ {this_str} | awk -F_ '{print $ 2}' `
C =` echo $ {this_str} | awk -F_ '{print $ 3}' '
... e così via ...