ฉันมีสตริง:
one_two_three_four_five
ฉันต้องการบันทึกในA
ค่าตัวแปรtwo
และB
ค่าตัวแปรfour
จากสตริงด้านบน
ฉันมีสตริง:
one_two_three_four_five
ฉันต้องการบันทึกในA
ค่าตัวแปรtwo
และB
ค่าตัวแปรfour
จากสตริงด้านบน
คำตอบ:
ใช้cut
กับ_
เป็นตัวคั่นสนามและได้รับเขตข้อมูลที่ต้องการ:
A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"
คุณยังสามารถใช้echo
และไพพ์แทนสตริงที่นี่:
A="$(echo 'one_two_three_four_five' | cut -d'_' -f2)"
B="$(echo 'one_two_three_four_five' | cut -d'_' -f4)"
ตัวอย่าง:
$ 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]*
คุณรู้ไหมว่าเกิดอะไรขึ้นที่นี่
echo "${s##*_}"
ใช้ POSIX sh constructs เพียงอย่างเดียวคุณสามารถใช้การสร้างทดแทนพารามิเตอร์เพื่อแยกวิเคราะห์ทีละตัว โปรดทราบว่ารหัสนี้ถือว่ามีจำนวนฟิลด์ที่จำเป็นไม่เช่นนั้นฟิลด์สุดท้ายจะถูกทำซ้ำ
string='one_two_three_four_five'
remainder="$string"
first="${remainder%%_*}"; remainder="${remainder#*_}"
second="${remainder%%_*}"; remainder="${remainder#*_}"
third="${remainder%%_*}"; remainder="${remainder#*_}"
fourth="${remainder%%_*}"; remainder="${remainder#*_}"
อีกทางหนึ่งคุณสามารถใช้การแทนที่พารามิเตอร์ที่ไม่ได้กล่าวถึงโดยปิดใช้งานการขยายไวด์การ์ดและIFS
ตั้งค่าเป็นอักขระตัวคั่น (ใช้ได้เฉพาะในกรณีที่ตัวคั่นเป็นอักขระที่ไม่ใช่ช่องว่างเดียวหรือหากลำดับช่องว่างใด ๆ เป็นตัวคั่น)
string='one_two_three_four_five'
set -f; IFS='_'
set -- $string
second=$2; fourth=$4
set +f; unset IFS
สิ่งนี้ปิดบังพารามิเตอร์ตำแหน่ง หากคุณทำสิ่งนี้ในฟังก์ชันเฉพาะพารามิเตอร์ตำแหน่งของฟังก์ชันเท่านั้นที่จะได้รับผลกระทบ
อีกวิธีหนึ่งคือการใช้read
builtin
IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF
unset IFS
ไม่ได้กลับIFS
ไปเป็นค่าเริ่มต้น หากหลังจากนั้นบางคนOldIFS="$IFS"
จะมีค่า Null ใน OldIFS นอกจากนี้ยังสมมติว่าค่าก่อนหน้าของ IFS เป็นค่าเริ่มต้นซึ่งเป็นไปได้มาก (และมีประโยชน์) ที่จะไม่เป็น ทางออกที่ถูกต้องเพียงอย่างเดียวคือการจัดเก็บold="$IFS"
และกู้คืนในภายหลังด้วย IFS = "$ old" หรือ ... (...)
ใช้เปลือกย่อย หรือดีกว่ายังอ่านคำตอบของฉัน
unset IFS
ไม่ได้คืนค่าIFS
เป็นค่าเริ่มต้น แต่จะคืนค่าการแบ่งเขตข้อมูลเป็นผลเริ่มต้น ใช่มันเป็นข้อ จำกัด แต่โดยทั่วไปแล้วเป็นข้อปฏิบัติที่ยอมรับได้ ปัญหาเกี่ยวกับ subshell คือเราต้องดึงข้อมูลออกมา read
ฉันจะแสดงวิธีการแก้ปัญหาที่ไม่ได้เปลี่ยนสถานะในตอนท้ายด้วย (ใช้งานได้ในเชลล์ POSIX แต่ IIRC ไม่ได้อยู่ในเชลล์เป้าหมายเพราะมันจะทำงานread
ในเชลล์ย่อยเนื่องจากเอกสารที่นี่) การใช้<<<
คำตอบที่คุณใช้เป็นตัวแปรที่ใช้งานได้เฉพาะใน ksh / bash / zsh
user/my_folder/[this_is_my_file]*
อย่างไร สิ่งที่ฉันได้รับเมื่อฉันทำตามขั้นตอนเหล่านี้คือ[this_is_my_file]*
/
ไม่มีของพวกเขาจะทำอะไรเป็นพิเศษเกี่ยวกับ
ต้องการที่จะเห็นawk
คำตอบดังนั้นนี่คือหนึ่ง:
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'
วิธีที่ง่ายที่สุด (สำหรับเชลล์ที่มี <<<) คือ:
IFS='_' read -r a second a fourth a <<<"$string"
การใช้ตัวแปรชั่วคราว$a
แทน$_
เพราะเชลล์หนึ่งตัวบ่น
ในสคริปต์เต็ม:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<<"$string"
echo "$second $fourth"
ไม่มีการเปลี่ยน IFS ไม่ใช่ปัญหากับset -f
(การขยายชื่อเส้นทาง) ไม่มีการเปลี่ยนแปลงพารามิเตอร์ตำแหน่ง ("$ @")
สำหรับวิธีแก้ปัญหาแบบพกพาสำหรับเชลล์ทั้งหมด (ใช่แล้ว POSIX ทั้งหมดรวมอยู่) โดยไม่ต้องเปลี่ยน IFS หรือset -f
ใช้ heredoc (ซับซ้อนกว่านี้เล็กน้อย) เทียบเท่า:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<-_EOF_
$string
_EOF_
echo "$second $fourth"
เข้าใจว่าโซลูชันนี้ (ทั้ง here-doc และการใช้<<<
จะลบ newlines ต่อท้ายทั้งหมด
และว่าสิ่งนี้ถูกออกแบบมาเพื่อเนื้อหาตัวแปร "หนึ่งซับ"
โซลูชั่นสำหรับ multi-liners เป็นไปได้ แต่ต้องการโครงสร้างที่ซับซ้อนมากขึ้น
วิธีการแก้ปัญหาที่ง่ายมากเป็นไปได้ใน bash รุ่น 4.4
readarray -d _ -t arr <<<"$string"
echo "array ${arr[1]} ${arr[3]}" # array numbers are zero based.
ไม่มีความเทียบเท่าสำหรับ POSIX เชลล์เนื่องจาก POSIX เชลล์จำนวนมากไม่มีอาร์เรย์
สำหรับเชลล์ที่มีอาร์เรย์อาจจะง่ายเหมือน:
(ทดสอบการทำงานใน attsh, lksh, mksh, ksh และ bash)
set -f; IFS=_; arr=($string)
แต่ด้วยการใช้งานที่เพิ่มมากขึ้นเพื่อเก็บและรีเซ็ตตัวแปรและตัวเลือก:
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]}"
ใน zsh อาร์เรย์เริ่มต้นที่ 1 และไม่แยกสตริงตามค่าเริ่มต้น
ดังนั้นจึงจำเป็นต้องทำการเปลี่ยนแปลงบางอย่างเพื่อให้การทำงานเป็นแบบ zsh นี้
read
ง่ายตราบใดที่ OP ไม่ต้องการแยกองค์ประกอบที่ 76 และ 127 ออกมาจากสตริงที่ยาว ...
readarray
อาจใช้งานได้ง่ายกว่าสำหรับสถานการณ์นั้น
ด้วยzsh
คุณสามารถแยกสตริง (บน_
) เป็นอาร์เรย์:
elements=(${(s:_:)string})
จากนั้นเข้าถึงแต่ละ / องค์ประกอบใด ๆ ผ่านดัชนีอาเรย์:
print -r ${elements[4]}
เก็บไว้ในใจว่าในzsh
(เหมือนksh
/ bash
) ดัชนีอาร์เรย์เริ่มต้นที่ 1
set -f
คำเตือนในโซลูชันแรก ... ดอกจัน*
อาจจะ?
set -f
? ฉันไม่ได้ใช้/read
IFS
ลองใช้วิธีแก้ปัญหาของฉันกับสตริง*_*_*
หรืออะไรก็ตาม ...
อนุญาตให้ใช้วิธีแก้ปัญหาแบบหลามหรือไม่
# python -c "import sys; print sys.argv[1].split('_')[1]" one_two_three_four_five
two
# python -c "import sys; print sys.argv[1].split('_')[3]" one_two_three_four_five
four
อีกตัวอย่าง awk; ง่ายต่อการเข้าใจ
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...
สามารถใช้กับตัวแปรได้
สมมติว่า:
this_str = "one_two_three_four_five"
จากนั้นทำงานต่อไปนี้:
A = `echo $ {this_str} | awk -F_ '{พิมพ์ $ 1}' `
B =` echo $ {this_str} | awk -F_ '{พิมพ์ $ 2}' `
C =` echo $ {this_str} | awk -F_ '{พิมพ์ $ 3}' '
... และอื่น ๆ ...