ฉันมีสตริง:
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
สิ่งนี้ปิดบังพารามิเตอร์ตำแหน่ง หากคุณทำสิ่งนี้ในฟังก์ชันเฉพาะพารามิเตอร์ตำแหน่งของฟังก์ชันเท่านั้นที่จะได้รับผลกระทบ
อีกวิธีหนึ่งคือการใช้readbuiltin
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}' '
... และอื่น ๆ ...