ฉันมีสตริงที่มีหลายคำอย่างน้อยหนึ่งช่องว่างระหว่างแต่ละสอง ฉันจะแยกสตริงออกเป็นแต่ละคำเพื่อให้สามารถวนซ้ำได้อย่างไร
สตริงถูกส่งเป็นอาร์กิวเมนต์ ${2} == "cat cat file"
เช่น ฉันจะวนซ้ำมันได้อย่างไร
นอกจากนี้ฉันจะตรวจสอบว่าสตริงมีช่องว่างได้อย่างไร
ฉันมีสตริงที่มีหลายคำอย่างน้อยหนึ่งช่องว่างระหว่างแต่ละสอง ฉันจะแยกสตริงออกเป็นแต่ละคำเพื่อให้สามารถวนซ้ำได้อย่างไร
สตริงถูกส่งเป็นอาร์กิวเมนต์ ${2} == "cat cat file"
เช่น ฉันจะวนซ้ำมันได้อย่างไร
นอกจากนี้ฉันจะตรวจสอบว่าสตริงมีช่องว่างได้อย่างไร
คำตอบ:
คุณลองส่งผ่านตัวแปรสตริงไปยังfor
ลูปหรือไม่ Bash สำหรับหนึ่งจะแยกบนช่องว่างโดยอัตโนมัติ
sentence="This is a sentence."
for word in $sentence
do
echo $word
done
This
is
a
sentence.
A=${A}${word})
คุณก็สามารถผนวกกับตัวแปร:
touch NOPE; var='* a *'; for a in $var; do echo "[$a]"; done
เอาท์พุท[NOPE] [a] [NOPE]
แทนที่จะเป็นที่คาดหวัง[*] [a] [*]
(LFs แทนที่ด้วย SPC เพื่อให้สามารถอ่านได้)
ฉันชอบการแปลงเป็นอาเรย์เพื่อให้สามารถเข้าถึงแต่ละองค์ประกอบ:
sentence="this is a story"
stringarray=($sentence)
ตอนนี้คุณสามารถเข้าถึงแต่ละองค์ประกอบได้โดยตรง (เริ่มต้นด้วย 0):
echo ${stringarray[0]}
หรือแปลงกลับเป็นสตริงเพื่อวนซ้ำ:
for i in "${stringarray[@]}"
do
:
# do whatever on $i
done
แน่นอนว่าการวนลูปผ่านสตริงได้รับคำตอบก่อนหน้านี้ แต่คำตอบนั้นมีข้อเสียที่จะไม่ติดตามองค์ประกอบแต่ละตัวเพื่อใช้ในภายหลัง:
for i in $sentence
do
:
# do whatever on $i
done
touch NOPE; var='* a *'; arr=($var); set | grep ^arr=
เอาท์พุทarr=([0]="NOPE" [1]="a" [2]="NOPE")
แทนที่คาดไว้arr=([0]="*" [1]="a" [2]="*")
เพียงใช้เชลล์ในตัว "ชุด" ในตัว ตัวอย่างเช่น,
กำหนด $ text
หลังจากนั้นคำแต่ละคำใน $ text จะเป็น $ 1, $ 2, $ 3 และอื่น ๆ เพื่อความแข็งแรงมักจะมี
ชุด - ข้อความขยะ $ เปลี่ยน
เพื่อจัดการกรณีที่ $ text ว่างเปล่าหรือเริ่มต้นด้วยเส้นประ ตัวอย่างเช่น:
text = "นี่คือการทดสอบ" ชุด - ข้อความขยะ $ เปลี่ยน สำหรับคำ; ทำ echo "[$ word]" เสร็จแล้ว
ภาพพิมพ์นี้
[นี้] [คือ] [เป็น] [ทดสอบ]
awk
แต่set
ง่ายกว่ามาก ตอนนี้ฉันเป็นset
แฟนบอย ขอบคุณ @Idelic!
touch NOPE; var='* a *'; set -- $var; for a; do echo "[$a]"; done
ผลแทนการที่คาดว่าจะ [NOPE] [a] [NOPE]
ใช้งานได้เฉพาะในกรณีที่คุณแน่ใจว่า 101% แน่ใจว่าไม่มีอักขระเมเซล SHELL ในสตริงที่แยก! [*] [a] [*]
set -f
ก่อนset -- $var
และset +f
หลังได้
set -f
โซลูชันของคุณก็ปลอดภัยเช่นกัน แต่set +f
เป็นค่าเริ่มต้นของแต่ละเชลล์ดังนั้นจึงเป็นรายละเอียดที่สำคัญซึ่งจะต้องมีการบันทึกไว้เพราะคนอื่น ๆ อาจไม่ได้ตระหนักถึงมัน (เหมือนที่ฉันเคยเป็น)
วิธีที่ง่ายและปลอดภัยที่สุดใน BASH 3 ขึ้นไปคือ:
var="string to split"
read -ra arr <<<"$var"
(ซึ่งarr
เป็นอาร์เรย์ที่รับส่วนที่แยกของสตริง) หรือหากอาจมีการขึ้นบรรทัดใหม่ในอินพุตและคุณต้องการมากกว่าแค่บรรทัดแรก:
var="string to split"
read -ra arr -d '' <<<"$var"
(โปรดทราบว่าพื้นที่ใน-d ''
นั้นไม่สามารถถูกทิ้งไว้ได้) แต่สิ่งนี้อาจทำให้คุณได้รับ newline ที่ไม่คาดคิดจาก<<<"$var"
(เนื่องจากเป็นการเพิ่ม LF ในตอนท้าย)
ตัวอย่าง:
touch NOPE
var="* a *"
read -ra arr <<<"$var"
for a in "${arr[@]}"; do echo "[$a]"; done
แสดงผลที่คาดหวัง
[*]
[a]
[*]
เนื่องจากโซลูชันนี้ (ตรงกันข้ามกับโซลูชันก่อนหน้าทั้งหมดที่นี่) จึงไม่น่าจะเกิดความคาดไม่ถึงและมักจะไม่สามารถควบคุมเชลล์ได้
สิ่งนี้จะช่วยให้คุณมีพลังอย่างเต็มที่ของ IFS อย่างที่คุณต้องการ:
ตัวอย่าง:
IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd)
for a in "${arr[@]}"; do echo "[$a]"; done
เอาต์พุตคล้าย:
[tino]
[x]
[1000]
[1000]
[Valentin Hilbig]
[/home/tino]
[/bin/bash]
อย่างที่คุณเห็นช่องว่างสามารถรักษาด้วยวิธีนี้ได้เช่นกัน:
IFS=: read -ra arr <<<' split : this '
for a in "${arr[@]}"; do echo "[$a]"; done
เอาท์พุท
[ split ]
[ this ]
โปรดทราบว่าการจัดการIFS
ใน BASH นั้นเป็นหัวข้อของตัวเองดังนั้นการทดสอบของคุณจึงมีหัวข้อที่น่าสนใจเกี่ยวกับเรื่องนี้:
unset IFS
: ละเว้นการทำงานของ SPC, TAB, NL และบนบรรทัดเริ่มต้นและสิ้นสุดIFS=''
: ไม่ต้องแยกฟิลด์อ่านทุกอย่างIFS=' '
: Runs ของ SPC (และ SPC เท่านั้น)ตัวอย่างสุดท้าย
var=$'\n\nthis is\n\n\na test\n\n'
IFS=$'\n' read -ra arr -d '' <<<"$var"
i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done
เอาท์พุท
1 [this is]
2 [a test]
ในขณะที่
unset IFS
var=$'\n\nthis is\n\n\na test\n\n'
read -ra arr -d '' <<<"$var"
i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done
เอาท์พุท
1 [this]
2 [is]
3 [a]
4 [test]
BTW:
หากคุณไม่คุ้นเคยกับการ$'ANSI-ESCAPED-STRING'
ใช้มันก็เป็นไทม์เมอร์
หากคุณไม่รวม-r
(เหมือนในread -a arr <<<"$var"
) การอ่านจะมีเครื่องหมายแบ็กสแลชหนี นี่เป็นแบบฝึกหัดสำหรับผู้อ่าน
สำหรับคำถามที่สอง:
เพื่อทดสอบบางสิ่งบางอย่างในสตริงฉันมักจะติดcase
เช่นนี้สามารถตรวจสอบหลายกรณีพร้อมกัน (หมายเหตุ: กรณีเท่านั้นดำเนินการจับคู่แรกถ้าคุณต้องการใช้case
คำสั่งที่ใช้หลายข้อผิดพลาด) และความต้องการนี้ค่อนข้างบ่อย เจตนา):
case "$var" in
'') empty_var;; # variable is empty
*' '*) have_space "$var";; # have SPC
*[[:space:]]*) have_whitespace "$var";; # have whitespaces like TAB
*[^-+.,A-Za-z0-9]*) have_nonalnum "$var";; # non-alphanum-chars found
*[-+.,]*) have_punctuation "$var";; # some punctuation chars found
*) default_case "$var";; # if all above does not match
esac
ดังนั้นคุณสามารถตั้งค่าส่งคืนเพื่อตรวจสอบ SPC ดังนี้:
case "$var" in (*' '*) true;; (*) false;; esac
ทำไมcase
? เนื่องจากโดยปกติแล้วจะสามารถอ่านได้ง่ายกว่าลำดับของ regex และต้องขอบคุณ Shell Metacharacters ที่สามารถจัดการได้ 99% ของความต้องการทั้งหมดเป็นอย่างดี
set -f
หรือset -o noglob
เพื่อเปลี่ยนการวนรอบได้เช่นกันว่าอักขระเมตาเชลล์จะไม่เป็นอันตรายต่อบริบทนี้อีกต่อไป แต่ฉันไม่ได้เป็นเพื่อนของสิ่งนี้เพราะมันทิ้งพลังไว้มากมายของเปลือกหอย / เป็นข้อผิดพลาดอย่างมากที่จะเปลี่ยนการตั้งค่านี้กลับไปกลับมา
;&
ให้บรรลุเป้าหมายนั้นได้ ไม่แน่ใจใน bash รุ่นที่ปรากฏ ฉันเป็นผู้ใช้ 4.3
;&
เป็นข้อผิดพลาดที่บังคับโดยไม่มีการตรวจสอบรูปแบบเหมือนใน C และนอกจากนี้ยังมี;;&
เพียงที่จะทำการตรวจสอบรูปแบบเพิ่มเติม ดังนั้น;;
เป็นเหมือนif ..; then ..; else if ..
และ;;&
เป็นเหมือนif ..; then ..; fi; if ..
ที่;&
เป็นเหมือนm=false; if ..; then ..; m=:; fi; if $m || ..; then ..
- หนึ่งไม่เคยหยุดการเรียนรู้ (จากคนอื่น ๆ );)
;;&
ก่อนที่คุณจะแสดงความคิดเห็น: D ขอบคุณและอาจเปลือกอยู่กับคุณ;)
$ echo "This is a sentence." | tr -s " " "\012"
This
is
a
sentence.
สำหรับการตรวจสอบช่องว่างให้ใช้ grep:
$ echo "This is a sentence." | grep " " > /dev/null
$ echo $?
0
$ echo "Thisisasentence." | grep " " > /dev/null
$ echo $?
1
echo "X" |
สามารถมักจะถูกแทนที่ด้วยเช่นนี้<<<"X"
grep -s " " <<<"This contains SPC"
คุณสามารถมองเห็นความแตกต่างถ้าคุณทำสิ่งที่ชอบในทางตรงกันข้ามกับecho X | read var
read var <<< X
เฉพาะตัวแปรหลังที่นำเข้าvar
มาในเชลล์ปัจจุบันในขณะที่เข้าถึงมันในตัวแปรแรกคุณต้องจัดกลุ่มดังนี้:echo X | { read var; handle "$var"; }
(A)ในการแบ่งประโยคเป็นคำ (คั่นด้วยช่องว่าง) คุณสามารถใช้ IFS เริ่มต้นโดยใช้
array=( $string )
ตัวอย่างการเรียกใช้ตัวอย่างข้อมูลต่อไปนี้
#!/bin/bash
sentence="this is the \"sentence\" 'you' want to split"
words=( $sentence )
len="${#words[@]}"
echo "words counted: $len"
printf "%s\n" "${words[@]}" ## print array
จะส่งออก
words counted: 8
this
is
the
"sentence"
'you'
want
to
split
อย่างที่คุณเห็นคุณสามารถใช้อัญประกาศเดี่ยวหรือคู่ได้โดยไม่มีปัญหา
หมายเหตุ:
- นี่เป็นคำตอบเดียวกับม็อบแต่โดยวิธีนี้คุณจะเก็บอาร์เรย์สำหรับความต้องการเพิ่มเติมใด ๆ หากคุณต้องการเพียงลูปเดียวคุณสามารถใช้คำตอบของเขาซึ่งสั้นกว่าหนึ่งบรรทัด :)
- โปรดอ้างอิงคำถามนี้สำหรับวิธีอื่นในการแยกสตริงตามตัวคั่น
(B)ในการตรวจสอบอักขระในสตริงคุณยังสามารถใช้การจับคู่นิพจน์ทั่วไป
ตัวอย่างเพื่อตรวจสอบว่ามีอักขระช่องว่างที่คุณสามารถใช้ได้:
regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
then
echo "Space here!";
fi
สำหรับการตรวจสอบช่องว่างด้วยการทุบตี:
[[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"
echo $WORDS | xargs -n1 echo
เอาต์พุตนี้ทุกคำคุณสามารถประมวลผลรายการนั้นตามที่เห็นสมควร