วิธีการแยกสตริงด้วยอัญประกาศ (เช่นอาร์กิวเมนต์คำสั่ง) ในทุบตี?


7

ฉันมีสตริงเช่นนี้:

"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"

ฉันต้องการแยกมันออกเป็นดังนี้

aString that may haveSpaces IN IT
bar
foo
bamboo  
bam boo

ฉันจะทำอย่างไร (ควรใช้ซับเดียว)



@ David โพสต์คำถามที่แตกต่างกันค่อนข้างจริง
foxneSs

ไม่จริงๆมันเป็นปัญหาทั่วไปเดียวกัน
DavidPostill

@DavidPostill - ปัญหานี้ง่ายกว่ามาก: ทุกอย่างที่ต้องการคือ for l in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $l; done
AFH

@AFH ฮ่า ๆ ฉันเพิ่งโพสต์คำตอบนาน ความแตกต่างเพียงอย่างเดียวในการส่งออกคือเหมืองที่เก็บรักษาไว้ " s ฉันพลาดความจริงที่ว่า OP ไม่จำเป็นต้องใช้มันในเอาต์พุต
DavidPostill

คำตอบ:


3

เมื่อฉันเห็นคำตอบของ David Postill ฉันคิดว่า "ต้องมีวิธีแก้ปัญหาที่ง่ายกว่า" หลังจากการทดลองฉันพบงานต่อไปนี้: -

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo $string
eval 'for word in '$string'; do echo $word; done'

ใช้งานได้เพราะ eval ขยายบรรทัด (ลบเครื่องหมายคำพูดและขยาย string ) ก่อนดำเนินการบรรทัดผลลัพธ์ (ซึ่งเป็นคำตอบในบรรทัด):

for word in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $word; done

ทางเลือกที่ขยายไปยังบรรทัดเดียวกันคือ:

eval "for word in $string; do echo \$word; done"

ที่นี่ string ถูกขยายภายในเครื่องหมายคำพูดคู่ แต่ $ จะต้องหลบหนีเพื่อที่ word ไม่ขยายก่อนที่จะมีการดำเนินการบรรทัด ผลลัพธ์ที่ได้คือ: -

[~/]$ string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
[~/]$ echo $string
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
[~/]$ eval 'for word in '$string'; do echo $word; done'
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo
[~/]$ eval "for word in $string; do echo \$word; done"
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

4

ทางออกที่ง่ายที่สุดคือการใช้อาร์เรย์ของ args ที่ยกมาซึ่งคุณสามารถวนซ้ำถ้าคุณต้องการหรือส่งโดยตรงไปยังคำสั่ง

eval "array=($string)"

for arg in "${array[@]}"; do echo "$arg"; done   

ป.ล. กรุณาแสดงความคิดเห็นหากคุณพบวิธีที่ง่าย eval.

แก้ไข:

การสร้างคำตอบของ @Hubbitus เรามีเวอร์ชันที่ถูกสุขลักษณะและมีการอ้างอิงอย่างเหมาะสม หมายเหตุ: นี่คือ overkill และจริง ๆ แล้วจะปล่อยแบ็กสแลชเพิ่มเติมในส่วนที่อ้างถึงสองหรือครั้งเดียวก่อนเครื่องหมายวรรคตอนมากที่สุด

declare -a "array=($( echo "$string" | sed 's/[][`~!@#$%^&*():;<>.,?/\|{}=+-]/\\&/g' ))"

ฉันปล่อยให้ผู้อ่านที่สนใจเปลี่ยนไปตามที่เห็นสมควร http://ideone.com/FUTHhj


3

ฉันจะทำอย่างไร

$ for l in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $l; done
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

ฉันควรทำอย่างไรถ้าสตริงของฉันอยู่ใน bash ตัวแปร?

วิธีง่ายๆในการใช้ bash tokenizer สตริงจะไม่ทำงานตามที่แยกในทุกพื้นที่ไม่เพียง แต่คนนอกเครื่องหมายคำพูด

DavidPostill@Hal /f/test
$ cat ./test.sh
#! /bin/bash
string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
for word in $string; do echo "$word"; done

DavidPostill@Hal /f/test
$ ./test.sh
"aString
that
may
haveSpaces
IN
IT"
bar
foo
"bamboo"
"bam
boo"

ในการหลีกเลี่ยงปัญหานี้เชลล์สคริปต์ต่อไปนี้ (splitstring.sh) จะแสดงวิธีหนึ่ง:

#! /bin/bash 
string=$(cat <<'EOF'
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" 
EOF
)
echo Source String: "$string"
results=()
result=''
inside=''
for (( i=0 ; i<${#string} ; i++ )) ; do
    char=${string:i:1}
    if [[ $inside ]] ; then
        if [[ $char == \\ ]] ; then
            if [[ $inside=='"' && ${string:i+1:1} == '"' ]] ; then
                let i++
                char=$inside
            fi
        elif [[ $char == $inside ]] ; then
            inside=''
        fi
    else
        if [[ $char == ["'"'"'] ]] ; then
            inside=$char
        elif [[ $char == ' ' ]] ; then
            char=''
            results+=("$result")
            result=''
        fi
    fi
    result+=$char
done
if [[ $inside ]] ; then
    echo Error parsing "$result"
    exit 1
fi

echo "Output strings:"
for r in "${results[@]}" ; do
    echo "$r" | sed "s/\"//g"
done

เอาท์พุท:

$ ./splitstring.sh
Source String: "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
Output strings:
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

ที่มา: คำตอบ StackOverflow แยกสตริงด้วยช่องว่างที่อยู่นอกเครื่องหมายคำพูดเท่านั้น โดย choroba . สคริปต์ได้รับการปรับแต่งให้ตรงกับความต้องการของคำถาม


2

คุณสามารถทำได้ด้วย declare แทน eval, ตัวอย่างเช่น:

แทน:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo "Initial string: $string"
eval 'for word in '$string'; do echo $word; done'

ทำ:

declare -a "array=($string)"
for item in "${array[@]}"; do echo "[$item]"; done

แต่โปรดทราบว่ามันไม่ปลอดภัยกว่านี้หากข้อมูลเข้ามาจากผู้ใช้!

ดังนั้นถ้าคุณลองใช้คำว่า string ดังนี้

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'

คุณได้รับ hostname ประเมินแล้ว (อาจมีบางสิ่งที่คล้ายกัน rm -rf / )!

ความพยายามที่ง่ายมาก ๆ ที่จะปกป้องมันเพียงแค่แทนที่ตัวอักษรอย่าง backtrick `และ $

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'
declare -a "array=( $(echo $string | tr '`$<>' '????') )"
for item in "${array[@]}"; do echo "[$item]"; done

ตอนนี้คุณได้ผลลัพธ์เช่น:

[aString that may haveSpaces IN IT]
[bar]
[foo]
[bamboo]
[bam boo]
[?hostname?]

รายละเอียดเพิ่มเติมเกี่ยวกับวิธีการและข้อดีข้อเสียที่คุณอาจพบในคำตอบที่ดี: https://stackoverflow.com/questions/17529220/why-should-eval-be-avoided-in-bash-and-what-should-i-use-instead/17529221#17529221

แต่ก็ยังมีใบเวกเตอร์สำหรับการโจมตี ฉันต้องการได้ในวิธีทุบตีของข้อความสตริงเช่นในอัญประกาศคู่ (") แต่ไม่ตีความเนื้อหา .


0

ใช้ awk

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN {FPAT = "([^ ]+)|(\"[^\"]+\")"}{for(i=1;i<=NF;i++){gsub("\"","",$i);print $i} }'
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

หรือแปลงช่องว่างเป็น "% 20" หรือ "_" ดังนั้นจึงสามารถประมวลผลได้โดยใช้คำสั่งถัดไป

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN {FPAT = "([^ ]+)|(\"[^\"]+\")"}{for(i=1;i<=NF;i++){gsub("\"","",$i);gsub(" ","_",$i)} print }'
aString_that_may_haveSpaces_IN_IT bar foo bamboo bam_boo

อ้างอิง: Awk พิจารณาสตริงที่ยกมาสองครั้งเป็นโทเค็นเดียว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.