Jawaban:
Gunakan cut
dengan _
sebagai pembatas bidang dan dapatkan bidang yang diinginkan:
A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"
Anda juga bisa menggunakan echo
dan mem-pipe bukannya string Here:
A="$(echo 'one_two_three_four_five' | cut -d'_' -f2)"
B="$(echo 'one_two_three_four_five' | cut -d'_' -f4)"
Contoh:
$ 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]*
Apakah Anda tahu apa yang terjadi di sini?
echo "${s##*_}"
Hanya menggunakan konstruksi sh POSIX, Anda bisa menggunakan konstruksi substitusi parameter untuk mengurai satu pembatas pada satu waktu. Perhatikan bahwa kode ini mengasumsikan bahwa ada jumlah bidang yang diperlukan, jika tidak, bidang terakhir diulang.
string='one_two_three_four_five'
remainder="$string"
first="${remainder%%_*}"; remainder="${remainder#*_}"
second="${remainder%%_*}"; remainder="${remainder#*_}"
third="${remainder%%_*}"; remainder="${remainder#*_}"
fourth="${remainder%%_*}"; remainder="${remainder#*_}"
Atau, Anda dapat menggunakan substitusi parameter yang tidak dikutip dengan ekspansi wildcard dinonaktifkan dan IFS
diatur ke karakter pembatas (ini hanya bekerja jika pembatas adalah karakter non-spasi tunggal atau jika urutan spasi apa pun adalah pembatas).
string='one_two_three_four_five'
set -f; IFS='_'
set -- $string
second=$2; fourth=$4
set +f; unset IFS
Ini mengacaukan parameter posisi. Jika Anda melakukan ini dalam suatu fungsi, hanya parameter posisi fungsi yang terpengaruh.
Namun pendekatan lain adalah dengan menggunakan read
builtin.
IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF
unset IFS
tidak kembali IFS
ke default. Jika setelah itu seseorang OldIFS="$IFS"
akan memiliki nilai nol di dalam OldIFS. Juga, diasumsikan bahwa nilai IFS sebelumnya adalah default, yang sangat mungkin (dan berguna) untuk tidak. Satu-satunya solusi yang benar adalah menyimpan old="$IFS"
dan mengembalikan dengan IFS = "$ old". Atau ... gunakan sub-shell (...)
. Atau, lebih baik lagi, baca jawaban saya.
unset IFS
tidak mengembalikan IFS
ke nilai default, tetapi mengembalikan pemisahan bidang ke efek default. Ya, ini adalah batasan, tetapi biasanya yang dapat diterima dalam praktik. Masalah dengan subkulit adalah kita perlu mengambil data darinya. Saya memang menunjukkan solusi yang tidak mengubah keadaan pada akhirnya, dengan read
. (Ini bekerja di shell POSIX, tetapi IIRC tidak di shell Bourne karena ia akan menjalankan read
dalam subkulit karena dokumen di sini.) Menggunakan <<<
seperti pada jawaban Anda adalah varian yang hanya bekerja di ksh / bash / zsh.
user/my_folder/[this_is_my_file]*
? Apa yang saya dapatkan ketika saya mengikuti langkah-langkah ini adalah[this_is_my_file]*
/
.
Ingin melihat awk
jawaban, jadi ini satu:
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'
Cara paling sederhana (untuk kerang dengan <<<) adalah:
IFS='_' read -r a second a fourth a <<<"$string"
Menggunakan variabel temporal $a
alih-alih $_
karena satu shell mengeluh.
Dalam skrip lengkap:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<<"$string"
echo "$second $fourth"
Tidak ada perubahan IFS, bukan masalah dengan set -f
(Perluasan pathname) Tidak ada perubahan pada parameter posisi ("$ @").
Untuk solusi portabel untuk semua shell (ya, semua POSIX termasuk) tanpa mengubah IFS atau set -f
, gunakan (setara dengan sedikit lebih kompleks) heredoc:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<-_EOF_
$string
_EOF_
echo "$second $fourth"
Memahami bahwa solusi ini (baik di sini-doc dan penggunaan <<<
akan menghapus semua baris baru.
Dan bahwa ini dirancang untuk konten variabel "satu liner."
Solusi untuk multi-liner dimungkinkan tetapi membutuhkan konstruksi yang lebih kompleks.
Solusi yang sangat sederhana dimungkinkan di versi bash 4.4
readarray -d _ -t arr <<<"$string"
echo "array ${arr[1]} ${arr[3]}" # array numbers are zero based.
Tidak ada padanan untuk shell POSIX, karena banyak shell POSIX tidak memiliki array.
Untuk cangkang yang memiliki larik mungkin sesederhana:
(diuji bekerja di attsh, lksh, mksh, ksh, dan bash)
set -f; IFS=_; arr=($string)
Tetapi dengan banyak pipa tambahan untuk menjaga dan mengatur ulang variabel dan opsi:
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]}"
Di zsh, array dimulai pada 1, dan tidak memisah string secara default.
Jadi beberapa perubahan perlu dilakukan untuk mendapatkan ini berfungsi di zsh.
read
sederhana selama OP tidak ingin mengekstraksi elemen ke-76 dan ke-127 dari string panjang ...
readarray
bisa lebih mudah digunakan untuk situasi itu.
Dengan zsh
Anda dapat membagi string (on _
) menjadi array:
elements=(${(s:_:)string})
dan kemudian mengakses setiap elemen melalui indeks array:
print -r ${elements[4]}
Perlu diingat bahwa dalam indeks arrayzsh
(tidak seperti ksh
/ bash
) mulai dari 1 .
set -f
peringatan ke solusi pertama. ... tanda bintang *
mungkin?
set -f
? Saya tidak menggunakan read
/ IFS
. Coba solusi saya dengan string seperti *_*_*
atau apa pun ...
Contoh awk lainnya; lebih mudah dimengerti.
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...
Dapat digunakan dengan variabel juga.
Misalkan:
this_str = "one_two_three_four_five"
Maka yang berikut berfungsi:
A = `echo $ {this_str} | awk -F_ '{print $ 1}' `
B =` echo $ {this_str} | awk -F_ '{print $ 2}' `
C =` echo $ {this_str} | awk -F_ '{print $ 3}' `
... dan seterusnya ...