Câu trả lời:
Sử dụng cut
với _
dấu phân cách trường và nhận các trường mong muốn:
A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"
Bạn cũng có thể sử dụng echo
và đường ống thay vì chuỗi Here:
A="$(echo 'one_two_three_four_five' | cut -d'_' -f2)"
B="$(echo 'one_two_three_four_five' | cut -d'_' -f4)"
Thí dụ:
$ 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]*
Bạn có biết chuyện gì đang xảy ra ở đây không?
echo "${s##*_}"
Chỉ sử dụng các cấu trúc sh POSIX, bạn có thể sử dụng các cấu trúc thay thế tham số để phân tích một dấu phân cách tại một thời điểm. Lưu ý rằng mã này giả định rằng có số lượng các trường cần thiết, nếu không thì trường cuối cùng được lặp lại.
string='one_two_three_four_five'
remainder="$string"
first="${remainder%%_*}"; remainder="${remainder#*_}"
second="${remainder%%_*}"; remainder="${remainder#*_}"
third="${remainder%%_*}"; remainder="${remainder#*_}"
fourth="${remainder%%_*}"; remainder="${remainder#*_}"
Ngoài ra, bạn có thể sử dụng thay thế tham số không được trích dẫn với mở rộng ký tự đại diện bị vô hiệu hóa và IFS
được đặt thành ký tự phân cách (điều này chỉ hoạt động nếu dấu phân cách là một ký tự không phải khoảng trắng hoặc nếu bất kỳ chuỗi khoảng trắng nào là dấu phân cách).
string='one_two_three_four_five'
set -f; IFS='_'
set -- $string
second=$2; fourth=$4
set +f; unset IFS
Điều này làm tắc nghẽn các tham số vị trí. Nếu bạn làm điều này trong một hàm, chỉ các tham số vị trí của hàm bị ảnh hưởng.
Tuy nhiên, một cách tiếp cận khác là sử dụng read
nội dung.
IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF
unset IFS
không trở về IFS
mặc định. Nếu sau đó ai đó OldIFS="$IFS"
sẽ có một giá trị null bên trong OldIFS. Ngoài ra, giả định rằng giá trị trước đó của IFS là mặc định, điều này rất có thể (và hữu ích) là không thể. Giải pháp đúng duy nhất là lưu trữ trong old="$IFS"
và sau đó khôi phục bằng IFS = "$ old". Hoặc ... sử dụng vỏ phụ (...)
. Hoặc, tốt hơn, đọc câu trả lời của tôi.
unset IFS
không khôi phục IFS
về giá trị mặc định, nhưng nó trả về trường tách thành hiệu ứng mặc định. Vâng, đó là một hạn chế, nhưng thường là một chấp nhận được trong thực tế. Vấn đề với một subshell là chúng ta cần lấy dữ liệu từ nó. Tôi cho thấy một giải pháp không thay đổi trạng thái ở cuối, với read
. (Nó hoạt động trong vỏ POSIX, nhưng IIRC không có trong vỏ Bourne bởi vì nó sẽ chạy read
trong một subshell do đây-tài liệu.) Sử dụng <<<
như trong bạn trả lời là một biến thể mà chỉ hoạt động trong ksh / bash / zsh.
user/my_folder/[this_is_my_file]*
nào? Những gì tôi có được khi thực hiện theo các bước này là[this_is_my_file]*
/
.
Muốn xem awk
câu trả lời, vì vậy đây là một:
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'
Cách đơn giản nhất (đối với hệ vỏ có <<<) là:
IFS='_' read -r a second a fourth a <<<"$string"
Sử dụng một biến thời gian $a
thay $_
vì bởi vì một vỏ phàn nàn.
Trong một kịch bản đầy đủ:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<<"$string"
echo "$second $fourth"
Không thay đổi IFS, không xảy ra sự cố với set -f
(Mở rộng tên đường dẫn) Không thay đổi các tham số vị trí ("$ @").
Đối với một giải pháp di động cho tất cả các hệ vỏ (có, bao gồm tất cả POSIX) mà không thay đổi IFS hoặc set -f
, sử dụng tương đương (phức tạp hơn một chút):
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<-_EOF_
$string
_EOF_
echo "$second $fourth"
Hiểu rằng các giải pháp này (cả tài liệu ở đây và việc sử dụng <<<
sẽ loại bỏ tất cả các dòng mới.
Và nó được thiết kế theo nội dung biến "một lớp".
Các giải pháp cho nhiều lớp có thể nhưng cần các cấu trúc phức tạp hơn.
Một giải pháp rất đơn giản có thể có trong phiên bản bash 4.4
readarray -d _ -t arr <<<"$string"
echo "array ${arr[1]} ${arr[3]}" # array numbers are zero based.
Không có vỏ tương đương với POSIX, vì nhiều vỏ POSIX không có mảng.
Đối với hệ vỏ có mảng có thể đơn giản như:
(đã thử nghiệm làm việc trong attsh, lksh, mksh, ksh và bash)
set -f; IFS=_; arr=($string)
Nhưng với rất nhiều hệ thống ống nước bổ sung để giữ và thiết lập lại các biến và tùy chọn:
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]}"
Trong zsh, các mảng bắt đầu bằng 1 và không phân tách chuỗi theo mặc định.
Vì vậy, một số thay đổi cần phải được thực hiện để làm việc này trong zsh.
read
rất đơn giản miễn là OP không muốn trích xuất các phần tử thứ 76 và 127 từ một chuỗi dài ...
readarray
có thể dễ sử dụng hơn cho tình huống đó.
Với zsh
bạn có thể chia chuỗi (bật _
) thành một mảng:
elements=(${(s:_:)string})
và sau đó truy cập từng / bất kỳ phần tử nào thông qua chỉ mục mảng:
print -r ${elements[4]}
Hãy nhớ rằng trong zsh
(không giống ksh
/ bash
) các chỉ số mảng bắt đầu từ 1 .
set -f
cảnh báo cho giải pháp đầu tiên. ... dấu hoa thị *
có lẽ?
set -f
? Tôi không sử dụng read
/ IFS
. Hãy thử các giải pháp của tôi với một chuỗi như *_*_*
hoặc bất cứ điều gì ...
Một ví dụ khác về awk; hiểu đơn giản hơn
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...
Có thể được sử dụng với các biến cũng.
Giả sử:
this_str = "one_two_three_four_five"
Sau đó, các công việc sau:
A = `echo $ {this_str} | awk -F_ '{in $ 1}' `
B =` echo $ {this_str} | awk -F_ '{in $ 2}' `
C =` echo $ {this_str} | awk -F_ '{in $ 3}' `
... và cứ thế ...